给出如下循环:
for await(let line of readFileLineByLine('./long-file.txt')) {
const interestingFacts = await processLine(line);
}
readFileLineByLine
返回AsyncGenerator的地方,这将开始处理await processLine
返回之前或之后的第二行吗? (假设第二行在processLine
完成之前就准备好了)
如果没有,那么如何并行化呢? (以便可以同时处理许多行)
答案 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;}