在 javascript 中的另一个 fetch 中使用 fetch

IT技术 javascript promise es6-promise fetch-api
2021-02-25 04:34:49

我想获得一个 api,然后再调用另一个。在javascript中使用这样的代码是否明智?

fetch(url, {
 method: 'get',
 }).then(function(response) {  
  response.json().then(function(data) {  
    fetch(anotherUrl).then(function(response) {
      return response.json();
    }).catch(function() {
      console.log("Booo");
    });
  });  
}) 
.catch(function(error) {  
  console.log('Request failed', error)  
});
6个回答

Fetch 返回一个 promise,您可以链接多个 promise,并在第二个请求中使用第一个请求的结果,依此类推。

本示例使用SpaceX API获取最新发射的信息,找到火箭的 id,并获取火箭的信息。

const url = 'https://api.spacexdata.com/v4';

const result = fetch(`${url}/launches/latest`, { method: 'get' })
  .then(response => response.json()) // pass the data as promise to next then block
  .then(data => {
    const rocketId = data.rocket;

    console.log(rocketId, '\n');
  
    return fetch(`${url}/rockets/${rocketId}`); // make a 2nd request and return a promise
  })
  .then(response => response.json())
  .catch(err => {
    console.error('Request failed', err)
  })

// I'm using the result const to show that you can continue to extend the chain from the returned promise
result.then(r => {
  console.log(r.first_stage); // 2nd request result first_stage property
});
.as-console-wrapper { max-height: 100% !important; top: 0; }

我更喜欢较小的 JSON 输出,但不幸的是,我对使用的 URL 没有任何更好的建议。
2021-04-25 04:34:49
@Henke - 好主意。我已将其更改为仅显示 1st stage 属性。
2021-05-08 04:34:49
谢谢@Henke。更新到 spacex api 的 v4。
2021-05-09 04:34:49

嵌套fetch()调用没有问题这取决于您通过嵌套调用尝试实现的目标。

您也可以使用.then()来链接调用。另请参阅如何构建嵌套的 Promise

fetch(url)
.then(function(response) { 
  return response.json()
})
.then(function(data) {   
  // do stuff with `data`, call second `fetch`
  return fetch(data.anotherUrl)
})
.then(function(response) { 
  return response.json(); 
})
.then(function(data) {
  // do stuff with `data`
})
.catch(function(error) { 
  console.log('Requestfailed', error) 
});

这是人们在开始使用 Promises 时被绊倒的一个常见问题,包括我自己。不过,首先...

高兴您尝试使用新的Fetch API,但如果我是您,我现在会使用 XMLHttpRequest 实现,例如 jQuery AJAX 或 Backbone 的 jQuery 的覆盖实现.ajax(),如果您已经在使用这些库。原因是 Fetch API 仍然很新,因此在这个阶段是实验性的。

话虽如此,人们肯定会使用它,但我不会在我自己的生产代码中使用它,直到它脱离“实验”状态。

如果您决定继续使用fetch,则可以使用polyfill注意:您必须跳过额外的环节才能使错误处理正常工作,并从服务器接收 cookie。如果您已经在加载 jQuery 或使用 Backbone,那么现在就坚持使用它们;无论如何,并不完全可怕。

现在进入代码:

你想要一个扁平的结构,否则你就错过了 Promises 的重点。嵌套 Promise 是不明智的,因为 Promise 解决了嵌套异步回调(回调地狱)无法解决的问题。

只需使用更具可读性的代码结构,您就可以节省自己的时间和精力,并减少错误代码的产生。这不是全部,但它是游戏的一部分,可以这么说。

Promises 是关于让异步代码保留同步代码丢失的大部分属性,例如平面缩进和一个异常通道。

-- Petka Antonov(蓝鸟Promise图书馆)

// run async #1
asyncGetFn()
// first 'then' - execute more async code as an arg, or just accept results
// and do some other ops
.then(response => {
    // ...operate on response data...or pass data onto next promise, if needed
})
// run async #2
.then(asyncGetAnotherFn)
.then(response => {
    // ...operate on response data...or pass data onto next promise, if needed
})
// flat promise chain, followed by 'catch'
// this is sexy error handling for every 'then' above
.catch(err => {  
  console.error('Request failed', err) 
  // ...raise exeption...
  // ... or, retry promise... 
})
数据如何从一个 promise 链发送到下一个对读者有帮助。否则很棒的帖子。
2021-05-03 04:34:49

在javascript中使用这样的代码是否明智?

是的。你的代码没问题。
除了在第二个请求之后 fetch(anotherUrl).then(function(response) {,我将替换return response.json();response.json().then(function(data2) {– 就像在第一个请求之后一样。然后,
该变量data2将根据需要包含内部 URL 请求的响应正文。
这意味着 – 无论您想用 做什么data2,您都必须在第二个回调中进行(因为您不返回Promise。)
此外,多打印一些内容将有助于了解正在发生的事情。

1. 原代码——稍作修改

进行这些更改后,这是一个包含您的代码的堆栈片段: 1

const url = 'https://jsonplaceholder.typicode.com/todos/1';
const anotherUrl = 'https://jsonplaceholder.typicode.com/todos/4';
fetch(url, {
  method: 'get'
}).then(function (response) {
  response.json().then(function (data) {
    console.log('Response body of outer "url":');
    console.log(JSON.stringify(data) + '\n\n');
    fetch(anotherUrl).then(function (response) {
      response.json().then(function (data2) {
        console.log('Response body of inner "anotherUrl":');
        console.log(JSON.stringify(data2) + '\n\n');
      });
    }).catch(function () {
      console.log('Booo');
    });
  });
})
.catch(function (error) {
  console.log('Request failed', error);
});
.as-console-wrapper { max-height: 100% !important; top: 0; }

这真的很好,尽管现在用粗箭头样式定义函数更常见。

2. 代码重构

这是您的代码的重构版本。它有一个内部链接/嵌套请求 – fetch(urlInner)– 这取决于从前一个/外部请求中检索到的数据:fetch (urlOuter)
通过返回外部和内部 URL 获取的Promise,可以在代码稍后访问/解析Promise的结果: 2

const urlOuter = 'https://jsonplaceholder.typicode.com/todos/1';
let urlInner = '';
const resultPromise = fetch(urlOuter)
  .then(responseO => responseO.json())
  .then(responseBodyO => {
    console.log('The response body of the outer request:');
    console.log(JSON.stringify(responseBodyO) + '\n\n');
    const neededValue = responseBodyO.id + 3;
    urlInner = 'https://jsonplaceholder.typicode.com/todos/' + neededValue;
    console.log('neededValue=' + neededValue + ', URL=' + urlInner);
    return fetch(urlInner)
      .then(responseI => responseI.json())
      .then(responseBodyI => {
        console.log('The response body of the inner/nested request:');
        console.log(JSON.stringify(responseBodyI) + '\n\n');
        return responseBodyI;
      }).catch(err => {
        console.error('Failed to fetch - ' + urlInner);
        console.error(err);
      });
  }).catch(err => {
    console.error('Failed to fetch - ' + urlOuter);
    console.error(err);
  });

resultPromise.then(jsonResult => {
  console.log('Result - the title is "' + jsonResult.title + '".');
});
.as-console-wrapper { max-height: 100% !important; top: 0; }

请注意,缩进的深度不得超过八个空格。

3.这种代码风格的优点

这显然是编写代码嵌套风格——这意味着链接的请求fetch(urlInner)缩进并在第一个请求的回调中进行fetch(urlOuter)然而,缩进树是合理的,这种风格与我对链接请求的直觉产生了很好的共鸣。但更重要的是,这种风格可以编写错误消息,指出哪个 URL 失败

运行下面的代码片段以查看错误消息如何说明导致错误的是 内部/第二个 URL

const urlOuter = 'https://jsonplaceholder.typicode.com/todos/1';
let urlInner = '';
const resultPromise = fetch(urlOuter)
  .then(responseO => responseO.json())
  .then(responseBodyO => {
    console.log('The response body of the outer request:');
    console.log(JSON.stringify(responseBodyO) + '\n\n');
    const neededValue = responseBodyO.id + 3;
    urlInner = 'https://VERY-BAD-URL.typicode.com/todos/' + neededValue;
    console.log('neededValue=' + neededValue + ', URL=' + urlInner);
    return fetch(urlInner)
      .then(responseI => responseI.json())
      .then(responseBodyI => {
        console.log('The response body of the inner/nested request:');
        console.log(JSON.stringify(responseBodyI) + '\n\n');
        return responseBodyI;
      }).catch(err => {
        console.error('Failed to fetch - ' + urlInner);
        console.error(err);
      });
  }).catch(err => {
    console.error('Failed to fetch - ' + urlOuter);
    console.error(err);
  });

resultPromise.then(jsonResult => {
  console.log('Result - the title is "' + jsonResult.title + '".');
});
.as-console-wrapper { max-height: 100% !important; top: 0; }

4. 展平所有出现的.then()?

受其他人的启发,您可能会想将 的所有出现都展平 .then(),如下所示。

我建议要这样做——或者至少在做之前三思而后行。为什么?

  • 在没有错误的情况下,这无关紧要。
  • 如果错误的,这样的风格会迫使不太明显的错误信息:

const urlOuter = 'https://jsonplaceholder.typicode.com/todos/1';
let urlInner = '';
const resultPromise = fetch(urlOuter)
  .then(responseO => responseO.json())
  .then(responseBodyO => {
    console.log('The response body of the outer request:');
    console.log(JSON.stringify(responseBodyO) + '\n\n');
    const neededValue = responseBodyO.id + 3;
    urlInner = 'https://VERY-BAD-URL.typicode.com/todos/' + neededValue;
    console.log('neededValue=' + neededValue + ', URL=' + urlInner);
    return fetch(urlInner);
  })
  .then(responseI => responseI.json())
  .then(responseBodyI => {
    console.log('The response body of the inner/nested request:');
    console.log(JSON.stringify(responseBodyI) + '\n\n');
    return responseBodyI;
  }).catch(err => {
    console.error('Failed to fetch one or more of these URLs:');
    console.log(urlOuter);
    console.log(urlInner);
    console.log(err);
  });
.as-console-wrapper { max-height: 100% !important; top: 0; }

代码非常扁平,但最后捕获的错误无法决定 哪个URL 请求失败。


1此答案的所有片段都符合 JavaScript Semistandard Style
2关于行11 - return fetch(urlInner)-这是 容易忘记return取回。(我曾经忘记它,即使以后写这个答案。)如果你算了吧,resultPromise不包含在所有的任何Promise。代码段中的最后三行将失败——它们将不输出 任何内容结果完全失败!

我没有看到 async/await 的语法糖的答案,所以我发布了我的答案。

在javascript中获取“内部”另一个获取的另一种方法就像 -

try {
    const response = await fetch(url, {method: 'get'});
    const data = response.json();
    //use the data...
    const anotherResponse = await fetch(url, {method: 'get'});
    const anotherdata = anotherResponse.json();
    //use the anotherdata...
 } catch (error) {
    console.log('Request failed', error) ;
 }

所以实际上你一个接一个地调用 url 。

此代码将在异步上下文中工作。