是否等待当前迭代完成,然后再进行下一个迭代?

时间:2019-09-13 07:38:23

标签: javascript async-await

给出如下循环:

for await(let line of readFileLineByLine('./long-file.txt')) {
    const interestingFacts = await processLine(line);
}

readFileLineByLine返回AsyncGenerator的地方,这将开始处理await processLine返回之前或之后的第二行吗? (假设第二行在processLine完成之前就准备好了)

如果没有,那么如何并行化呢? (以便可以同时处理许多行)

2 个答案:

答案 0 :(得分:4)

仅在最后一次迭代完成后才开始下一次迭代。在$result = $conn->query($sql)->fetch_all(); //... foreach ($result as $row) { //... } // Repeat the same loop again without calling query again //foreach... 循环中通过await可以很容易地显示出这一点:

for

要并行化,您可以调用一个函数(但不要等待它解析)并将(新的)Promise推送到一个数组,然后在该数组上调用const asyncIterable = { [Symbol.asyncIterator]() { return { i: 0, next() { if (this.i < 3) { return Promise.resolve({ value: this.i++, done: false }); } return Promise.resolve({ done: true }); } }; } }; const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); (async function() { for await (let num of asyncIterable) { console.log('start ' + num); await delay(1000); console.log('end ' + num); } })();

Promise.all

(不幸的是,由于无法预先知道迭代器返回的项目数,因此不可能像const asyncIterable = { [Symbol.asyncIterator]() { return { i: 0, next() { if (this.i < 3) { return Promise.resolve({ value: this.i++, done: false }); } return Promise.resolve({ done: true }); } }; } }; const processNum = num => new Promise(resolve => setTimeout(resolve, 1000)); (async function() { console.log('start'); const proms = []; for await (let num of asyncIterable) { proms.push(processNum(num)); } await Promise.all(proms); console.log('all done (1000ms)'); })();那样看起来更简洁)

答案 1 :(得分:2)

您的for await循环是以下内容的语法糖:

let generator = readFileLineByLine();

while (true) {
    let promise = generator.next();
    let item = await promise;

    if (item.done)
        break;

    let line = item.value;
    await processLine(line)

}

因此答案是肯定的,readLine(nextLine)processLine(previousLine)完成之前不会开始。

如果您希望两个功能不相互依赖,则一种选择是使readFileLineByLine是非异步的,即仅yield待处理的Promise。正常使用此生成器,将then(processLine)附加到每个诺言,然后等待它们全部出现:

let promises = [];

for (let promise of readFileLineByLinePending())
    promises.push(promise.then(processLine))

await Promise.all(promises)

这是一个演示:

async function delay(n) {
    return new Promise(res => setTimeout(res, n))
}


async function processLine(s) {
    console.log('process BEGIN:', s)
    await delay(300);
    console.log('process END:', s)
}

async function* readFileLineByLine() {
    for (let i = 0; i < 6; i++) {
        console.log('read BEGIN', i)
        await delay(500);
        let t = await 'line' + i;
        console.log('read END', i)
        yield t;

    }
}

function* readFileLineByLinePending() {
    for (let i = 0; i < 6; i++) {
        console.log('readPending BEGIN', i)
        let t = delay(500).then(() => {
            console.log('readPending END', i);
            return 'line' + i;
        });
        yield t;

    }
}


async function main() {

    console.time('async gen')

    for await(let line of readFileLineByLine())
       await processLine(line)

    console.log('----------------------------------------')
    console.timeEnd('async gen')
    console.log('----------------------------------------')


    console.time('sync gen')

    let promises = [];

    for (let promise of readFileLineByLinePending())
        promises.push(promise.then(processLine))

    await Promise.all(promises)

    console.log('----------------------------------------')
    console.timeEnd('sync gen')
    console.log('----------------------------------------')
}

main().then(() => console.log('done'))
.as-console-wrapper {max-height:100% !important; top:0;}