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

Posted

技术标签:

【中文标题】在 javascript 中的另一个 fetch 中使用 fetch【英文标题】:using a fetch inside another fetch in javascript 【发布时间】:2017-04-20 05:55:12 【问题描述】:

我想获得一个 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)  
);

【问题讨论】:

你想达到什么目的?第二次调用是否依赖于第一次调用的响应数据? @guest271314 是的,第二次调用取决于第一次调用的响应数据。 您也可以使用.then()链接调用 如果你在每个 promise (then) 回调中都没有 return,那么你最终的 .catch() 将无法工作。 【参考方案1】:

Fetch 返回一个promise,你可以chain multiple promises,在第二个请求中使用第一个请求的结果,以此类推。

本示例使用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; 

【讨论】:

谢谢@Henke。已更新至 spacex api 的 v4。 我更喜欢较小的 JSON 输出,但不幸的是我没有更好的 URL 使用建议。 @Henke - 好主意。我已将其更改为仅显示 1st stage 属性。 如果我需要来自第一个 fetch 的更多属性怎么办?它在链式提取中仍然可用吗?为什么return fetch 有一个then 而不是2 个thens?第二次提取中的data 对象在哪里?我的用例:使用来自 github api 的一些数据获取我的所有 repos,并使用另一个 api - return fetch - 从 iso 转换为德国日期格式。因此,我需要通过 repos 及其属性(如日期和名称)循环返回获取。 @Timo - 您可以链接多个 catch 然后阻止 - 请参阅此 article。您的 then(response => return response.json()) 是语法错误。箭头函数自动返回。你可能有大括号,这就是你需要返回的原因。【参考方案2】:

嵌套fetch() 调用没有问题。这取决于您试图通过嵌套调用来实现什么。

您也可以使用.then() 链接调用。另见How to structure nested Promises

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) 
);

【讨论】:

【参考方案3】:

这是人们在开始使用 Promises 时经常遇到的一个问题,包括我自己在内。但是,首先...

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

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

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

现在进入代码:

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

只需使用更易读的代码结构,您就可以节省时间和精力,并减少错误代码。这不是一切,但可以说是游戏的一部分。

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

-- Petka Antonov (Bluebird Promise Library)

// 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... 
)

【讨论】:

数据如何从一个承诺链发送到下一个承诺链将对读者有所帮助。否则很棒的帖子。 我部分同意@Si8,但在fetch 处看不到有关该问题的任何有价值的信息。【参考方案4】:

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

是的。您的代码很好。 除了第二次请求之后, fetch(anotherUrl).then(function(response) , 我会替换return response.json();response.json().then(function(data2) - 就像在 第一个请求。 然后变量data2 将包含内部的响应主体 URL 请求,根据需要。 这意味着 – 无论您想对 data2 做什么,您都必须这样做 在第二个回调中(因为您没有返回承诺。) 此外,再打印几份将有助于了解正在发生的事情。

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。代码重构

这是您的代码的重构版本。 它有一个内部 chained/nested 请求 – fetch(urlInner) – 取决于从先前/外部请求中检索到的数据:fetch (urlOuter)。 通过返回外部和内部 URL 获取的承诺, 稍后可以在代码中访问/解决承诺的结果: 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; 

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

3。这种代码风格的优点

这显然是一种嵌套的代码编写方式——意味着 链式请求fetch(urlInner) 缩进并在 第一个请求的回调fetch(urlOuter)。 然而,缩进树是合理的,这种风格很能引起共鸣 根据我对链接请求的直觉。 – 但更重要的是, 这种风格使得编写精确定位的错误消息成为可能 哪个 URL 失败

运行下面的 sn -p 以查看错误消息如何表明它是 导致错误的内部/第二个 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 此答案的所有 sn-ps 均符合 JavaScript Semistandard Style.2 关于第 11 行 – return fetch(urlInner) – 它是 非常很容易忘记return 获取。 (我曾经忘记它,甚至在写完这个答案之后。) 如果您确实忘记它,resultPromise 将不会包含任何承诺 全部。 然后 sn-p 中的最后三行将失败——它们将输出 什么都没有。 结果完全失败!

【讨论】:

【参考方案5】:

我没有看到带有 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。

此代码将在异步上下文中运行。

【讨论】:

【参考方案6】:

我建议使用axios,这样会更好,而且您不必处理 JSON 格式。此外,代码看起来更简洁,更易于理解。

axios.get(firstUrl).then((resp1) => 
   // Handle success of first api
   axios.get(secondUrl).then((resp2) => 
      return resp2.data                 
   ).catch((error) =>  /* Handle error of second api */ );
).catch((error) =>  /* Handle error of first api */ );

来自 LogRocket.com 的块引用:

与 Fetch 一样,Axios 也是基于 Promise 的。但是,它提供了更多 强大而灵活的功能集。

使用 Axios 优于原生 Fetch API 的优势包括:

请求和响应拦截 简化的错误处理 XSRF 防护 支持上传进度 响应超时 取消请求的能力 支持旧版浏览器 自动 JSON 数据转换

【讨论】:

【参考方案7】:

我会使用 fetches 数组或 url 数组,两者都按照您希望执行它们的顺序。然后使用reduce依次执行。这样它的可扩展性就更高了。

const urls = [
  'https://api.spacexdata.com/v4/launches/latest',
  'https://api.spacexdata.com/v4/launches/latest',
  'https://api.spacexdata.com/v4/launches/latest'
];

// handle the fetch logic
// and handle errors
const handleFetch = async (url) => 
  const resp = await fetch(url).catch(console.error);
  return resp.json()


// reduce fetches, receives the response
// of the previous, log it (and maybe use it as input)
const reduceFetch = async (acc, curr) => 
  const prev = await acc;
  console.log('previous call:', prev);

  return handleFetch(curr);


const pipeFetch = async urls => urls.reduce(reduceFetch, Promise.resolve(''));

pipeFetch(urls).then(console.log);

【讨论】:

以上是关于在 javascript 中的另一个 fetch 中使用 fetch的主要内容,如果未能解决你的问题,请参考以下文章

需要帮助将 c-strings 从嵌入式 SQL fetch 复制到单独结构中的另一个 c-string

JavaScript 中的 Promises/Fetch:如何从文本文件中提取文本

Javascript - 从浏览器中的另一个模块设置“让”变量

javascript 在Express.JS中的Node.JS中使用Fetch API

XSS小记:Javascript 中 eval 与 fetch组合在攻击场景中的使用

Javascript - 如何知道Fetch API中的响应类型?