Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/479.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 具有fetch-in-chain和Promise.all的多个API调用_Javascript_Reactjs_Fetch_Es6 Promise - Fatal编程技术网

Javascript 具有fetch-in-chain和Promise.all的多个API调用

Javascript 具有fetch-in-chain和Promise.all的多个API调用,javascript,reactjs,fetch,es6-promise,Javascript,Reactjs,Fetch,Es6 Promise,我在函数中进行API调用以获取一些数据。 然后,我需要根据第一次调用的特定值,对返回的每个数据项进行多个API调用。渲染状态时出现问题,从多个承诺添加的值在渲染期间不存在 到目前为止,我有这样的想法: fetch(url) .then(resp => resp.json()) .then(data => { //do some calculations and populate new array and return it }) .then(data => { co

我在函数中进行API调用以获取一些数据。 然后,我需要根据第一次调用的特定值,对返回的每个数据项进行多个API调用。渲染状态时出现问题,从多个承诺添加的值在渲染期间不存在

到目前为止,我有这样的想法:

fetch(url)
.then(resp => resp.json())
.then(data => {
  //do some calculations and populate new array and return it
})
.then(data => {
   const newData = [...data];
   Promise.all(functionWhichReturnsArrayOfPromises).then(el => {
      // more calculation
      newData.push(el.someValue);
   })
   return newData;
})
.then(data => this.setState({data: data}))
返回承诺数组的函数如下所示:

fetchMoreData(arr) {
   const promises = arr.map(el => {
      return fetch(someUrl)
      .then(data => data.json())
      .then(data => data)
   })
   return promises;
}

我认为我将承诺与其他承诺联系起来是不好的,有没有推荐一种更优雅的方法来实现这一点?

你说你的方法不好是正确的,原因如下:

.then(data => {
   const newData = [...data];
   Promise.all(fetchMoreData(newData)).then(el => {
      // more calculation
      newData.push(el.someValue);
   })
   return newData;
})
return newData
发生在您到达
newData.push(el.someValue)
之前,由于它们引用相同的数组,这意味着您正在调用
setState()
并传递一个异步变异的数组,该数组与组件重新呈现的时间无关

本质上,您已经创建了一个竞争条件,该条件将使组件状态不确定,因为它基于React框架决定重新呈现组件之前还是之后
fetch()
操作是否完成


为了解决这个问题,我们想到了两个选项,所以选择对您来说更可读的选项,或者与您的编码风格一致的选项,但是首先让我们进行一个小的重构,使您的助手函数更规范

异步函数应首选向数组返回承诺,而不是向承诺数组返回承诺:

fetchMoreData(arr) {
   const promises = arr.map(el =>
     fetch(someUrl)
       .then(res => res.json())
   );

   return Promise.all(promises);
}
考虑到这一点,让我们继续讨论两种解决方案:

嵌套(并返回)依赖于上一个作用域的承诺链 请注意,我们
返回fetchMoreData(array)。然后(…)
,在嵌套的continuation中,也
返回array
。这将允许
数组
传递到下一个链中的
数据

通过扁平承诺链从上一个作用域传递依赖关系
这里,我们将依赖项封装在另一个
Promise.all()
中,并将
数组
Promise
传递给下一个扁平链,然后使用数组解构语法在回调参数中再次分离出来。从这里开始,我们执行额外的计算,然后将数组传递到最后一个链。

我想我可能错过了您要做的事情。如果您只需要在单个项的“更多数据”承诺解决时访问这些项,那么在关闭该函数时很容易捕获这些数据

const fetchMoreData=(thingy)=>
取回(`https://example.com/api/thingy/${thingy.id}`)
.then(res=>res.json())
.then(res=>({…thingy,…res}))//注意:`thingy`和`res`都在范围内
取('https://example.com/api/thingy')
.then(res=>res.json())
.then(thingies=>Promise.all(thingies.map(fetchMoreData)))
.then(allThingies=>console.log(`Results:${JSON.stringify(allThingies)}`)
//.catch(…)

//用于测试的fetch的虚拟版本
const fetch=(url,opts)=>Promise.resolve({
json:()=>{
const result=(url.endsWith('thingy'))
?[{id:1,x:'a'},{id:2,x:'b'},{id:3,x:'c'}]
:{更多:['''foo','bar','baz'][url.slice(url.lastIndexOf('/')+1)]
log(`fetch('${url}')~~>${JSON.stringify(result)}`)
返回结果
}

})
返回
承诺。所有
结果将正确延迟
设置状态。现在,正如Patrick所说,它在数组进入状态后对其进行了变异。一个次要的问题是:
。然后(data=>data)
什么都不添加。这看起来比我试图做的要复杂一些,我真正需要的是从第一次获取中获取一些数据,根据这些数据创建新的数组并填充它,然后对新数组的每个元素进行API调用,获取一些额外的数据,并将其添加到上一步的每个数组元素中。我相信这正是它所做的。忽略
fetch
调用,这只是在没有任何实际API的情况下进行测试的一种方法。我们可能会编写一个简单的版本来代替。更新了第二个示例。我希望,它应该表现出简单性。
fetch(url)
  .then(res => res.json())
  .then(data => {
    // do some calculations and populate new array and return it
  })
  .then(array => {
    // nest the promise chain
    return fetchMoreData(array).then(result => {
      // more calculations dependent on array from previous scope
      result.forEach(el => {
        array.push(el.someValue);
      });

      // pass array along
      return array;
    });
  })
  .then(data => {
    this.setState({ data });
  });
fetch(url)
  .then(res => res.json())
  .then(data => {
    // do some calculations and populate new array and return it
  })
  .then(array => {
    const promise = fetchMoreData(array);
    // pass the dependency along
    return Promise.all([array, promise]);
  })
  // destructure dependencies
  .then(([array, result]) => {
    // more calculations dependent on array from previous scope
    result.forEach(el => {
      array.push(el.someValue);
    });

    // pass array along
    return array;
  })
  .then(data => {
    this.setState({ data });
  });