如何在异步功能中处理多个等待

时间:2019-11-12 09:14:45

标签: javascript async-await fetch

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

我用如下所示的await编写了异步功能-

前两个应该一个接一个地运行,但是第三个可以独立运行,而不必等待前两个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();

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

4 个答案:

答案 0 :(得分:2)

有很多方法,但是最通用的方法是将每个异步代码路径包装在一个异步函数中。您可以根据需要灵活地混合和匹配异步返回值。在您的示例中,您甚至可以内联异步iife的代码:

await Promise.all([
  (async() => {
    let getToken = await fetch(url_for_getToken);
    let getTokenData = await getToken.json();

    let writeToDB = await fetch(url_for_writeToDB);
    let writeToDBData = await writeToDB.json();
  })(),
  (async() => {
    let frontEnd = await fetch(url_for_frontEnd);
    let frontEndData = await frontEnd.json();
  })()
]);

答案 1 :(得分:1)

您可以使用.then(),而不要等待:

fetch(url_for_getToken)
  .then(getToken => getToken.json())
  .then(async getTokenData => {
    let writeToDB = await fetch(url_for_writeToDB);
    let writeToDBData = await writeToDB.json();
    // Carry on
  })

fetch(url_for_frontEnd)
  .then(frontEnd => frontEnd.json())
  .then(frontEndData => {
    // Carry on  
  })

答案 2 :(得分:1)

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

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

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

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())

并在称为seriesparallel的地方使用内联延续:

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

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

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; }

答案 3 :(得分:-2)

在开始且没有writeToDB的情况下运行独立请求(await

let writeToDB = fetch(url_for_writeToDB);

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

// ...