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

JS async functions execute in random order

我有一个异步函数发出 api 请求。完成后,它会调用一个不同的异步函数,发出 4 次 api 请求(旨在按调用顺序进行)。但每次运行时,那些 api 请求 return 随机顺序的数据。

首先,我调用 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 数组状态(在数组中的顺序正确)。

每次运行时,所有营养结果都会 return 在 nutritionResults 数组中以随机顺序编辑,我假设这是因为 handleSearch 没有调用正确顺序的功能。或者我遗漏了另一个问题。

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

我认为你使用 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)
   ...
}

问题是 handleSearch() 不是 awaiting 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)})
    // ...
}

注意:fetchNutrution()前面的return很重要,如果你想让await等待它完成。否则await不会等待,因为没有Promise return被.then().

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

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() 到 return 一个可以等待的 Promise。

现在您还需要允许 fetchSearch()await handleSearch()。与上面类似,您可以添加一个 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)