首页 > 解决方案 > 如何在异步函数中处理多个等待

问题描述

我有多个 API 调用要进行,它们通过 API 获取,通过 API 将数据写入 DB,通过另一个 API 将输出发送到前端。

我已经用 await 编写了异步函数,如下所示 -

前 2 个应该一个接一个地运行,但第三个可以独立运行,无需等待前 2 个 fetch 语句完成。

let getToken= await fetch(url_for_getToken);
let getTokenData = await getToken.json();

let writeToDB = await fetch(url_for_writeToDB);
let writeToDBData = await writeToDB.json();

let frontEnd = await fetch(url_for_frontEnd);
let frontEndData = await frontEnd.json();

处理此类多个 fetch 语句的最佳方法是什么?

标签: javascriptasync-awaitfetch

解决方案


如果您使用 Promise “创建者”(= 返回 Promise 的函数)而不是原始 Promise,它会更容易。首先,定义:

const fetchJson = (url, opts) => () => fetch(url, opts).then(r => r.json())

它返回这样一个“创造者”。现在,这里有两个用于串行和并行链接的实用程序,它们同时接受原始承诺和“创建者”:

const call = f => typeof f === 'function' ? f() : f;

const parallel = (...fns)  => Promise.all(fns.map(call));

async function series(...fns) {
    let res = [];

    for (let f of fns)
        res.push(await call(f));

    return res;
}

然后,主要代码可以这样写:

let [[getTokenData, writeToDBData], frontEndData] = await parallel(
    series(
        fetchJson(url_for_getToken),
        fetchJson(url_for_writeToDB),
    ),
    fetchJson(url_for_frontEnd),
)

如果您不喜欢专用的“创建者”包装器,您可以fetchJson正常定义

const fetchJson = (url, opts) => fetch(url, opts).then(r => r.json())

series并在或被parallel调用的地方使用内联延续:

let [[getTokenData, writeToDBData], frontEndData] = await parallel(
    series(
        () => fetchJson('getToken'),
        () => fetchJson('writeToDB'),
    ),
    () => fetchJson('frontEnd'), // continuation not necessary, but looks nicer
)

为了进一步推动这个想法,我们也可以制造seriesparallel回报“创造者”,而不是承诺。这样,我们可以构建串行和并行承诺的任意嵌套“电路”并按顺序获得结果。完整的工作示例:

const call = f => typeof f === 'function' ? f() : f;

const parallel = (...fns)  => () => Promise.all(fns.map(call));

const series = (...fns) => async () => {
    let res = [];

    for (let f of fns)
        res.push(await call(f));

    return res;
};

//

const request = (x, time) => () => new Promise(resolve => {
    console.log('start', x);
    setTimeout(() => {
        console.log('end', x)
        resolve(x)
    }, time)
});

async function main() {
    let chain = series(
        parallel(
            series(
                request('A1', 500),
                request('A2', 200),
            ),
            series(
                request('B1', 900),
                request('B2', 400),
                request('B3', 400),
            ),
        ),
        parallel(
            request('C1', 800),
            series(
                request('C2', 100),
                request('C3', 100),
            )
        ),
    );

    let results = await chain();

    console.log(JSON.stringify(results))
}

main()
.as-console-wrapper { max-height: 100% !important; top: 0; }


推荐阅读