【问题标题】:using a fetch inside another fetch in javascript在 javascript 中的另一个 fetch 中使用 fetch
【发布时间】: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() 将无法工作。

标签: javascript promise es6-promise fetch-api


【解决方案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);

          【讨论】:

            猜你喜欢
            • 2019-03-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-01-26
            • 2021-07-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多