Javascript 后台循环
Javascript background loop
假设我们有一个 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
如何重写此代码以在后台循环 运行 时读取和处理文件?
我的解决方案
我想到的是这个:
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
}
确实记录了:
loop started
read file started
file processing started
timer fires
loop finished
loop result processing started
有没有更简单、更简洁的方法来做到这一点?我的解决方案有哪些缺点?
你的代码的第一个版本阻止进一步处理的原因是 await
得到了一个立即解决的承诺(值 null
被包装在一个承诺中,就像你做了 await Promise.resolve(null)
).这意味着 await
之后的代码将在 当前 "task" 期间恢复:它只是将一个微任务推送到任务队列中,该微任务将在同一任务中被消耗。您等待的所有其他异步内容都在任务队列中等待,而不是微任务队列。
setTimeout
是这样,readFile
也是这样。他们的回调在任务队列中挂起,因此不会优先于 await
s 生成的微任务。
所以你需要一种方法让 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 = "https://images.pexels.com/photos/34950/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="">
假设我们有一个 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
如何重写此代码以在后台循环 运行 时读取和处理文件?
我的解决方案
我想到的是这个:
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
}
确实记录了:
loop started
read file started
file processing started
timer fires
loop finished
loop result processing started
有没有更简单、更简洁的方法来做到这一点?我的解决方案有哪些缺点?
你的代码的第一个版本阻止进一步处理的原因是 await
得到了一个立即解决的承诺(值 null
被包装在一个承诺中,就像你做了 await Promise.resolve(null)
).这意味着 await
之后的代码将在 当前 "task" 期间恢复:它只是将一个微任务推送到任务队列中,该微任务将在同一任务中被消耗。您等待的所有其他异步内容都在任务队列中等待,而不是微任务队列。
setTimeout
是这样,readFile
也是这样。他们的回调在任务队列中挂起,因此不会优先于 await
s 生成的微任务。
所以你需要一种方法让 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 = "https://images.pexels.com/photos/34950/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="">