如何运行 并行执行两个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 并行转换为 运行...我不太了解如何实现此目的:

问题:我们如何更新下面的代码,使barfoo并行运行,因此,输出有超过 2 个可能的结果?

注意:这纯粹是为了学习目的...我想看到竞争条件的发生。

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 到 运行 多个异步任务,它将等待所有异步任务解决并 return 一个输出数组。

Promise.all 通过迭代(串行)执行异步任务,技术上不是并行执行它们,但它们是 运行 并行执行。当所有异步任务都解决或拒绝(如果其中任何一个失败)时,它会给你一个结果。

或者你可以 运行 他们 1 对 1 而不是 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 之前结束(或抛出) s.

例如,给定 setTimeout 计划在接下来的 1-2 秒内(随机)发生的 1000 个任务,并且您还在 1.5 秒后调用以下函数:

const fn = () => {
  console.log('1');
  for (let i = 0; i < 1e8; i++) {
  }
  console.log('2');
};

一旦 fn 启动,它将在下一个随机函数 运行 之前 运行 所有代码(同步)。所以即使随机函数调用了console.log,仍然可以保证,在上面的情况下,2会在1.

之后被记录。

所以,在你原来的例子中,只有两种可能性:

  • bar 的回调先 运行s,并完全结束,然后 foo 的回调 运行s,并完全结束。或者:
  • foo 的回调首先 运行s,并完全结束,然后 bar 的回调 运行s,并完全结束。

没有别的可能,即使随机超时落在完全相同的数字上。在任何给定时间,控制流只能位于代码中的 一处