等待多个并发等待操作

Ben*_*Ben 31 javascript promise async-await es2017

如何更改以下代码,以便触发异步操作并同时运行?

const value1 = await getValue1Async();
const value2 = await getValue2Async();
// use both values
Run Code Online (Sandbox Code Playgroud)

我需要做这样的事情吗?

const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
// use both values
Run Code Online (Sandbox Code Playgroud)

T.J*_*der 46

TL; DR

不要在你得到承诺的问题中使用该模式,然后单独等待它们; 相反,使用Promise.all(至少现在):

const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
Run Code Online (Sandbox Code Playgroud)

虽然您的解决方案确实并行运行了两个操作,但如果两个承诺都拒绝,它就无法正确处理拒绝.

细节:

您的解决方案并行运行它们,但在等待第二个之前总是等待第一个完成.如果你只想启动它们,并行运行它们,并得到两个结果,就可以了. (不,它不是,继续读...)请注意,如果第一个完成(比方说)五秒钟完成而第二个在一秒钟内失败,那么您的代码将等待整整五秒钟然后失败.

遗憾的是,目前await没有进行并行等待的语法,所以你有你列出的尴尬,或者Promise.all.(虽然有一天会讨论await.all或类似的 ;也许有一天.)

Promise.all版本是:

const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
Run Code Online (Sandbox Code Playgroud)

...更简洁,并且如果第二次操作快速失败,也不等待第一次操作完成(例如,在上面的五秒钟/一秒钟的例子中,上面将在一秒钟内拒绝而不是等待五秒钟) .另请注意,使用原始代码,如果第二个承诺在第一个承诺解决之前拒绝,您可能会在控制台中出现"未处理的拒绝"错误(您当前使用的是Chrome v61),尽管该错误可能是虚假的(因为您,最终处理拒绝).但如果两个承诺都拒绝,你将得到一个真正的未处理拒绝错误,因为控制流永远不会到达async,因此p2拒绝永远不会被处理.

未处理的拒绝是一件坏事™(以至于很快,NodeJS就会在真正无法处理的拒绝过程中中止这个过程,就像未处理的异常一样 - 因为它们就是这样),所以最好避免"获得承诺然后const value2 = await p2;它"模式你的问题.

以下是故障情况下的时间差异示例(使用500毫秒和100毫秒而不是5秒和1秒),还可能是虚假的未处理拒绝错误(打开真正的浏览器控制台来查看):

const getValue1Async = () => {
  return new Promise(resolve => {
    setTimeout(resolve, 500, "value1");
  });
};
const getValue2Async = () => {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 100, "error");
  });
};

// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
  try {
    console.time("separate");
    const p1 = getValue1Async();
    const p2 = getValue2Async();
    const value1 = await p1;
    const value2 = await p2;
  } catch (e) {
    console.error(e);
  }
  console.timeEnd("separate");
})();

// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
  try {
    console.time("Promise.all");
    const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
  } catch (e) {
    console.timeEnd("Promise.all", e);
  }
}, 1000);
Run Code Online (Sandbox Code Playgroud)
Open the real browser console to see the unhandled rejection error.
Run Code Online (Sandbox Code Playgroud)

在这里我们拒绝这两个,awaitp1导致非虚假的未处理拒绝错误p2:

const getValue1Async = () => {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 500, "error1");
  });
};
const getValue2Async = () => {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 100, "error2");
  });
};

// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
  try {
    console.time("separate");
    const p1 = getValue1Async();
    const p2 = getValue2Async();
    const value1 = await p1;
    const value2 = await p2;
  } catch (e) {
    console.error(e);
  }
  console.timeEnd("separate");
})();

// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
  try {
    console.time("Promise.all");
    const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
  } catch (e) {
    console.timeEnd("Promise.all", e);
  }
}, 1000);
Run Code Online (Sandbox Code Playgroud)
Open the real browser console to see the unhandled rejection error.
Run Code Online (Sandbox Code Playgroud)


在评论中你问过:

附带问题:以下部队是否会等待(并丢弃结果)p2

与原始代码一样,这与承诺拒绝有相同的问题:它会等到await p1 && await p2结算,即使p1先前拒绝; 如果p2p2结算前拒绝,它可能会产生一个可论证的虚假未处理拒绝错误; 和它产生如果两个真正未处理拒绝错误p1p1拒绝(因为p2的拒绝从未处理).

这是p2解决和p1拒绝的情况:

const getValue1Async = () => {
  return new Promise(resolve => {
    setTimeout(resolve, 500, false);
  });
};
const getValue2Async = () => {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 100, "error");
  });
};

(async () => {
  try {
    const p1 = getValue1Async();
    const p2 = getValue2Async();
    console.log("waiting");
    await p1 && await p2;
  } catch (e) {
    console.error(e);
  }
  console.log("done waiting");
})();
Run Code Online (Sandbox Code Playgroud)
Look in the real console (for the unhandled rejection error).
Run Code Online (Sandbox Code Playgroud)

......并拒绝:

const getValue1Async = () => {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 500, "error1");
  });
};
const getValue2Async = () => {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 100, "error2");
  });
};

(async () => {
  try {
    const p1 = getValue1Async();
    const p2 = getValue2Async();
    console.log("waiting");
    await p1 && await p2;
  } catch (e) {
    console.error(e);
  }
  console.log("done waiting");
})();
Run Code Online (Sandbox Code Playgroud)
Look in the real console (for the unhandled rejection error).
Run Code Online (Sandbox Code Playgroud)

  • @Ben:你们和我刚刚编辑的"Promise.all"之间有一个重要的区别,就是我们. (3认同)
  • "(以至于很快,NodeJS将在未处理的拒绝中止流程,就像未处理的异常 - 因为它们就是这样)" - 弃用警告措辞是不幸的,我后悔 - 但我们将_never_杀死代码上的节点上面 - 我们将:A)做出未处理的拒绝基于GC的B)警告GC错过了_lally long pending_操作,可能C)只有在我们能够证明拒绝未处理时才会杀死Node.js(第一种情况).我意识到情况令人困惑,我为此道歉 - 我们会做得更好. (2认同)
  • 在`await p1 && await p2`中,如果p1和p2都拒绝,那么p2是未处理的拒绝(并且基于GC的检测仍将正确地杀死该过程).我只讨论p2拒绝的情况,而p1仍处于未决状态. (2认同)
  • 在这种情况下,@ TJCrowder"我们的代码"是Node项目.特别是这是我参与的代码区域 - 对于模棱两可而言.我们是这样做的:https://github.com/nodejs/node/blob/master/lib/internal/process/promises.js - 有https://github.com/nodejs/node/pull/15126和https://github.com/nodejs/node/pull/15335关于正在进行的工作.在Chrome中,您可以在https://github.com/nwjs/chromium.src/blob/4657efa55cd1c6a3df5cb63210ab0dbf920364a9/third_party/WebKit/Source/bindings/core/v8/RejectedPromises.cpp#L49上查看V8绑定,该程序在ProcessQueue上运行一个任务. (2认同)
  • 也就是说,没有人做"基于GC"_yet_(firefox曾经做过,不确定他们是否仍然这样做) - BridgeAR的PR显示了我们现在正在考虑的方法.区域也可能是一个有趣的想法. (2认同)

Kai*_*Kai 7

我认为这应该有效:

 const [value1, value2] = await Promise.all([getValue1Async(),getValue2Async()]);
Run Code Online (Sandbox Code Playgroud)

如果有助于理解,下面会有一个更详细的例子:

const promise1 = async() => {
  return 3;
}

const promise2 = async() => {
  return 42;
}

const promise3 = async() => {
  return 500;
  // emulate an error
  // throw "something went wrong...";
}

const f1 = async() => {

  try {
    // returns an array of values
    const results = await Promise.all([promise1(), promise2(), promise3()]);
    console.log(results);
    console.log(results[0]);
    console.log(results[1]);
    console.log(results[2]);

    // assigns values to individual variables through 'array destructuring'
    const [value1, value2, value3] = await Promise.all([promise1(), promise2(), promise3()]);

    console.log(value1);
    console.log(value2);
    console.log(value3);

  } catch (err) {
    console.log("there was an error: " + err);
  }

}

f1();
Run Code Online (Sandbox Code Playgroud)

  • 我明白了.恕我直言,它应该工作:).对不起我的粗心确认 (2认同)