JS异步函数按随机顺序执行



我有一个异步函数发出api请求。一旦完成,它会调用一个不同的异步函数,发出4次api请求(按照调用的顺序进行)。但每次运行时,这些api请求都会以随机顺序返回数据。

首先,我调用fetchSearch,它按预期工作。

const fetchSearch = async () => {
var myHeaders = new Headers();
myHeaders.append("x-app-id", "...");
myHeaders.append("x-app-key", "...");

var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};

await fetch(`https://trackapi.nutritionix.com/v2/search/instant?query=${search}`, requestOptions)
.then(response => response.text())
.then(
result => (
handleSearch(JSON.parse(result).common)
)
)
.catch(error => console.log('error', error));
}

这调用handleSearch。我可能在这里做错了什么。

const handleSearch = (data) => {
const dataList=[]
for (var key in data) {
if (data.hasOwnProperty(key)) {
dataList.push(data[key])
}
}
if (dataList[0] !== undefined) {
setSearchResults([dataList[0], dataList[1], dataList[2], dataList[3]])
fetchNutrition(dataList[0].food_name)
.then(() => {fetchNutrition(dataList[1].food_name)})
.then(() => {fetchNutrition(dataList[2].food_name)})
.then(() => {fetchNutrition(dataList[3].food_name)})
} else {
setSearchError(true)
}
}

handleSearch调用fetchNutrition:

const fetchNutrition = async (foodName) => {
var h = new Headers();
h.append("accept", "application/json");
h.append("x-app-id", "...");
h.append("x-app-key", "...");
h.append("x-remote-user-id", "1");
h.append("Content-Type", "application/json");
var requestOptions = {
method: 'POST',
headers: h,
body: JSON.stringify({ "query": foodName }),
redirect: 'follow'
}
await fetch("https://trackapi.nutritionix.com/v2/natural/nutrients", requestOptions)
.then(response => response.text())
.then(result => {setNutritionResults(nutritionResults => [...nutritionResults, JSON.parse(result)])})
.catch(error => console.log('error', error))
}

对于来自fetchSearchhandleSearchfetchNutrition的4个字符串的数组,应该遍历每个字符串,并将相应的营养JSON字符串添加到nutritionResults数组状态(按数组中的正确顺序)。

每次运行时,所有营养结果都会以随机顺序返回到nutritionResults数组中,我认为这是因为handleSearch没有按正确顺序调用函数。或者我错过了另一个问题。

例如,如果fetchSearch返回["apple", "apples", "apple juice", "apple pie"],则nutritionResults将以长度为4的数组结束,但每次运行时都是随机顺序。

问题是handleSearch()不是await,而是fetchNutrition()。因此,代码会继续执行,而不需要等待——正如所描述的那样。

要解决此问题,您只需等待fetchNutrition()完成:

const handleSearch = async (data) => {
// ...
await fetchNutrition(dataList[0].food_name)
.then(() => {return fetchNutrition(dataList[1].food_name)})
.then(() => {return fetchNutrition(dataList[2].food_name)})
.then(() => {return fetchNutrition(dataList[3].food_name)})
// ...
}

注意:如果您希望await等待它完成,那么fetchNutrution()前面的return非常重要。否则CCD_ 21将不会等待它,因为CCD_ 23的链没有返回CCD_。

或者,这做了完全相同的事情:

const handleSearch = (data) => {
// ...
return fetchNutrition(dataList[0].food_name)
.then(() => {return fetchNutrition(dataList[1].food_name)})
.then(() => {return fetchNutrition(dataList[2].food_name)})
.then(() => {return fetchNutrition(dataList[3].food_name)})
// NOTE: The bugfix is adding a "return" 
// ...
}

以上任何一种情况都将导致handleSearch()返回可等待的Promise。

现在您还需要允许fetchSearch()awaithandleSearch()。与上面类似,您可以添加一个return来完成此操作:

await fetch(`https://trackapi.nutritionix.com/v2/search/instant?query=${search}`, requestOptions)
.then(response => response.text())
.then(
result => (
return handleSearch(JSON.parse(result).common)
// Note: The bugfix is adding the return above
// so that this chain of .then() will return the
// handleSearch() Promise which the above await
// will wait for
)
)

或者,您也可以执行以下操作以允许fetchSearch()等待handleSearch():

await fetch(`https://trackapi.nutritionix.com/v2/search/instant?query=${search}`, requestOptions)
.then(response => response.text())
.then(
result => handleSearch(JSON.parse(result).common)
)
// Note: The bugfix is removing the braces "()" around
// handleSearch() causing js to add an implicit "return"

另一种选择是避免将async/await与.then()链完全混合:

const response = await fetch(`https://trackapi.nutritionix.com/v2/search/instant?query=${search}`, requestOptions)
const result = await response.text()
await handleSearch(JSON.parse(result).common)

我认为您使用的await比promise.then更短。试试这个:

const handleSearch = async (data) => {
...
await fetchNutrition(dataList[1].food_name)
await fetchNutrition(dataList[2].food_name)
await fetchNutrition(dataList[3].food_name)
...
}

最新更新