提问人:Armeen Moon 提问时间:4/13/2020 最后编辑:Armeen Moon 更新时间:4/13/2020 访问量:994
如何并行运行两个 setTimeout 任务?
How to run two setTimeout tasks in parallel?
问:
我正在阅读 YDKJS,早期我们正在讨论异步代码、并行代码和并发代码之间的区别。
我有一个简单的异步示例:
let output = 0;
const bar = (cb) => setTimeout(cb, Math.random() * 1000);
const foo = (cb) => setTimeout(cb, Math.random() * 1000);
bar( () => {
output = 1;
});
foo( () => {
output = 2
});
setTimeout(() => {
// This Async code should have 2 different outputs
output;
}, 2000);
上面的代码可以有基于 Math.random 计时器和可变输出的 2 个答案:
但是,我想增加一点复杂性,并将 foo 和 bar 转换为并行运行......我对如何实现这一目标不太了解:
问题:我们如何更新下面的代码,以便并行运行,因此输出具有 2 个以上可能的结果?bar
foo
注意:这纯粹是出于学习目的......我想看到比赛条件的发生。
let inputA = 10;
let inputB = 11;
const bar = (cb) => setTimeout(cb, Math.random() * 1000);
const foo = (cb) => setTimeout(cb, Math.random() * 1000);
bar( () => {
inputA++;
inputB = inputB * inputA;
inputA = inputA + 3;
});
foo( () => {
inputB--;
inputA = 8 + inputB;
inputB = inputA * 2;
});
setTimeout(() => {
// This Parallel code should have more than 2 outputs;
console.log(inputA, inputB);
}, 2000);
答:
对于您的原始问题,您希望看到它们并行运行。您可以使用 Promise.all 运行多个异步任务,它将等待所有异步任务解析并返回一个输出数组。
Promise.all 通过迭代(串联)来执行异步任务,从技术上讲不是并行执行它们,而是并行运行。当所有异步任务都解决或拒绝时,如果其中任何一个任务失败,它将为您提供结果。
或者,您可以在没有 Promise.all 的情况下逐个运行它们。它们不会相互阻塞,所以仍然是并行的,但你 Promise.all 只是帮助你在一个地方处理回调结果。
输出将为 12 或 20,具体取决于您为 bar 和 foo 函数设置的随机超时。
对于争用条件,只有 setTimeout 函数是异步的,但回调中的所有操作都是同步且非阻塞的,因此线程不会从一个回调中的操作跳转到另一个回调,除非该回调中的所有操作都已完成。
但是在 JS 中,使用 SharedArrayBuffer 时仍然可以进行数据争用,这需要 Atomics 对象来防止数据争用。
let output = 0;
let inputA = 10;
let inputB = 11;
const bar = (cb) => setTimeout(cb, Math.random() * 1000);
const foo = (cb) => setTimeout(cb, Math.random() * 1000);
bar( () => {
inputA++;
inputB = inputA;
output = inputA + 1;
});
foo( () => {
inputB--;
inputA = inputB;
output = inputB * 2;
});
Promise.all([bar(),foo()])
.then(output => console.log('foo and bar tasks finished with output ',output));
setTimeout(() => {
console.log('output variable value: ', output)
}, 2000);
评论
幸运的是,您要调用的竞争条件在普通 Javascript 中是不可能的。任何时候,只要你有一个同步函数,一旦控制流被传递给该函数,该函数就绝对保证在环境中的任何其他 Javascript 运行之前运行到最后(或抛出)。
例如,给定 1000 个任务,这些任务计划在接下来的 1-2 秒内(随机)发生,并且您还在 1.5 秒后调用以下函数:setTimeout
const fn = () => {
console.log('1');
for (let i = 0; i < 1e8; i++) {
}
console.log('2');
};
启动后,它将在下一个随机函数运行之前(同步)运行其所有代码。所以即使随机函数调用,仍然可以保证,在上述情况下,会立即被记录下来。fn
console.log
2
1
因此,在原始示例中,只有 2 种可能性:
bar
的回调首先运行,并完全完成,然后 的回调运行,并完全完成。或:foo
foo
的回调首先运行,并完全完成,然后 的回调运行,并完全完成。bar
即使随机超时落在完全相同的数字上,也不可能有其他可能。在任何给定时间,控制流只能位于代码中的一个位置。
评论
bar
foo