在 javascript 中的另一个提取中使用提取

using a fetch inside another fetch in javascript

我想要一个 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)  
});

获取returns一个promise,就可以chain multiple promises,在第2个请求中使用第1个请求的结果,以此类推。

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

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

您也可以使用 .then() 来链接调用。另见

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 仍然很新,因此在现阶段处于试验阶段。

话虽如此,人们肯定会使用它,但在它退出 "experimental" 状态之前,我不会在自己的生产代码中使用它。

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

现在进入代码:

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

您将节省自己的时间和精力,并通过简单地使用更具可读性的代码结构来产生更少的错误代码。这不是全部,但可以说是游戏的一部分。

Promises are about making asynchronous code retain most of the lost properties of synchronous code such as flat indentation and one exception channel.

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

Is it wisely using a code like this in javascript?

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

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

确实不错,虽然 fat arrow 样式更常见 这些天来定义一个函数。

2。代码重构

这是您的代码的重构版本。 它有一个内部 chained/nested 请求 – fetch(urlInner) – 那 取决于从 previous/outer 请求中检索到的数据:fetch (urlOuter).
通过 return 外部和内部 URL 获取的承诺, 可以在代码后面 access/resolve 承诺的结果: 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 失败了

运行 下面的代码片段,看看错误消息是如何表明它是 inner/second 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将不包含任何承诺 全部。 代码片段中的最后三行将失败——它们将输出 。 结果彻底失败!

我没有看到 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 之后一个接一个地调用 url。

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

我建议使用 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:

As with Fetch, Axios is promise-based. However, it provides a more powerful and flexible feature set.

Advantages of using Axios over the native Fetch API include:

  • Request and response interception
  • Streamlined error handling
  • Protection against XSRF
  • Support for upload progress
  • Response timeout
  • The ability to cancel requests
  • Support for older browsers
  • Automatic JSON data transformation

我会使用一个提取数组或一个 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);

只是一些方法。

1,使用async -await

 app.get("/getemployeedetails/:id", async (req, res) => {
  const id = req.params.id;
  const employeeUrl = "http://localhost:3000/employee/" + id;
  try {
    const response = await fetch(employeeUrl);
    const employee = await response.json();

    const projectUrl = "http://localhost:3000/project/" + employee.project_id;
    const response1 = await fetch(projectUrl);
    const project = await response1.json();

    const result = {
      ...employee,
      ...project,
    };
    res.send(result);
  } catch (error) {
    console.log("getData: ", error);
  }
});

2, then

的链接
app.get("/getemployeedetails/:id", (req, res) => {
  const id = req.params.id;
  const employeeUrl = "http://localhost:3000/employee/" + id;
  let employeeResponse = null;
  fetch(employeeUrl)
    .then((employee) => employee.json())
    .then((resp) => {
        employeeResponse = resp
      const projectUrl =
        "http://localhost:3000/project/" + employeeResponse.project_id;
      return fetch(projectUrl);
    })
    .then((project) => project.json())
    .then((projectResponse) => {
      const result = {
        ...employeeResponse,
        ...projectResponse,
      };
      res.send(result);
    })
    .catch((err) => console.log(err));
});

3,以更好的方式链接

app.get("/getemployeedetails/:id", (req, res) => {
  const id = req.params.id;
  getEmployeeResponse(id).then((employeeResponse) => {
    getProjectResponse(employeeResponse.project_id)
      .then((projectResponse) => {
        const result = {
          ...employeeResponse,
          ...projectResponse,
        };
        res.send(result);
      })
      .catch((err) => console.log(err));
  });
});

function getEmployeeResponse(id) {
  return new Promise((resolve, reject) => {
    const employeeUrl = "http://localhost:3000/employee/" + id;
    fetch(employeeUrl)
      .then((employee) => employee.json())
      .then((resp) => resolve(resp))
      .catch((err) => reject(err));
  });
}

function getProjectResponse(id) {
  return new Promise((resolve, reject) => {
    const projectUrl = "http://localhost:3000/project/" + id;
    fetch(projectUrl)
      .then((project) => project.json())
      .then((resp) => resolve(resp))
      .catch((err) => reject(err));
  });
}

你来决定。