Javascript 具有fetch-in-chain和Promise.all的多个API调用
我在函数中进行API调用以获取一些数据。 然后,我需要根据第一次调用的特定值,对返回的每个数据项进行多个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
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 });
});