为了账号安全,请及时绑定邮箱和手机立即绑定

如何并行运行 Promise 链

如何并行运行 Promise 链

泛舟湖上清波郎朗 2022-01-07 20:43:48
我通过 MDN 文档找到了以下代码。var resolveAfter2Seconds = function() {  console.log("starting slow promise");  return new Promise(resolve => {    setTimeout(function() {      resolve(20);      console.log("slow promise is done");    }, 2000);  });};var resolveAfter1Second = function() {  console.log("starting fast promise");  return new Promise(resolve => {    setTimeout(function() {      resolve(10);      console.log("fast promise is done");    }, 1000);  });};var parallel = function() {  console.log('==PARALLEL with Promise.then==');  resolveAfter2Seconds().then((message)=>console.log(message));  resolveAfter1Second().then((message)=>console.log(message));}作为执行的结果,我认为每个 Promise 都将是没有顺序保证的并行执行。但是我写了下面的代码,似乎可以保证顺序。const promiseFunction1 = () => {    return new Promise((resolve) => {        let result = 0;        for (let i = 0; i < 1000000000; i++) {            result += 1;        }        resolve(result);    })}const promiseFunction2 = () => {    return new Promise((resolve) => {        let result = 0;        for (let i = 0; i < 10; i++) {            result += 1;        }        resolve(result);    })}const parallel = () => {    promiseFunction1()        .then(res => console.log(res));    promiseFunction2()        .then(res => console.log(res));}这两个代码有什么区别?
查看完整描述

2 回答

?
小怪兽爱吃肉

TA贡献1852条经验 获得超1个赞

Promise 构造函数是完全同步的(除非你调用其他异步的东西,比如setTimeout或另一个 Promise)。在第二个代码中,同步promiseFunction1运行并立即调用resolve。对promiseFunction2. 因此,创建的第一个 Promise(这里是 from promiseFunction1)将在第二个之前解决,无论每个promiseFunction运行的同步操作可能是昂贵的。

promiseFunction1承诺立即首先解析,如此这般到microtask队列第一,那么其相关的.then运行第一。

昂贵的同步代码通常不是在主线程上运行的好主意,因为它会阻塞 UI - 如果您遇到这种情况,请考虑将其移至工作线程,如果可能的话。

对于您的第一个代码:

我当时认为每个承诺都是没有顺序保证的并行执行。

因为resolveAfter2Seconds2 秒后resolveAfter1Second解析,1 秒后解析,所以几乎可以保证 1 秒 Promise 将首先解析。

下面的两个代码是一样的吗?

一点也不。首先,promiseFunctionparallel被调用时,两者都会立即运行。在第二种情况下,promiseFunction2仅在promiseFunction1解决后运行。


查看完整回答
反对 回复 2022-01-07
?
吃鸡游戏

TA贡献1829条经验 获得超7个赞

说Promise链通常意味着一系列then()调用,明确地意图按顺序运行它们,但是以异步方式,等待步骤之间的某些事件。


而不是编写一系列深入的嵌套回调......


function delay2s(callback){

  setTimeout(callback,2000);

}


delay2s(()=>{

  log.innerText="1.";

  delay2s(()=>{

    log.innerText+=" 2.";

    delay2s(()=>{

      log.innerText+=" 3.";

      delay2s(()=>{

        log.innerText+=" 4.";

        delay2s(()=>{

          log.innerText+=" 5.";

          delay2s(()=>{

            log.innerText+=" and Done.";

          });

        });

      });

    });

  });

});

<span id="log">Soon</span>

...你可以保持在一个“理智”的水平:


function delay2s(){

  return new Promise(resolve=>setTimeout(()=>resolve(),2000));

}


delay2s().then(()=>{

  log.innerText="1.";

  return delay2s();

}).then(()=>{

  log.innerText+=" 2.";

  return delay2s();

}).then(()=>{

  log.innerText+=" 3.";

  return delay2s();

}).then(()=>{

  log.innerText+=" 4.";

  return delay2s();

}).then(()=>{

  log.innerText+=" 5.";

  return delay2s();

}).then(()=>{

  log.innerText+=" and Done.";

});

<span id="log">Soon</span>

而且拥有多个独立的Promises并不代表它们一起运行,而是它们一起等待。

当然,这种情况下也有一些不错的辅助工具。就像等待一些事件,并在所有事件都完成后做某事可能看起来像一些讨厌的、命名的回调加上一些更讨厌的计数器:


let counter=5;

function tryfinal(){

  if(--counter>0)return;

  log.innerText+=" all Done.";

}


for(let i=0;i<counter;i++){

  setTimeout(()=>{

    log.innerText=(log.innerText=="Soon"?"":log.innerText+" ")+(i+1)+".";

    tryfinal();

  },4000*Math.random());

}

<span id="log">Soon</span>

或者它可以使用Promise.all/Settled():


let promises=[];

for(let i=0;i<5;i++){

  promises.push(new Promise(resolve=>setTimeout(()=>{

    log.innerText=(log.innerText=="Soon"?"":log.innerText+" ")+(i+1)+".";

    resolve();

  },4000*Math.random())));

}


Promise.all(promises).then(()=>log.innerText+=" and Done.");

<span id="log">Soon</span>

如果你想积极地并行做一些事情,你需要线程,这意味着web workers。此示例在两个线程上向上计数几秒钟,不时报告进度(在此期间它与 CPU 一起烹饪,您可以检查您所在操作系统的一些性能监控应用程序):


let limit=5000000000;

let report=100000000;

if(limit>Number.MAX_SAFE_INTEGER)

  log.innerText="No, you do not want that.";

else {

  let workertext="data:text/plain,"

      +escape("for(let i=0;i<="+limit+";i++)"

      +"  if(i%"+report+"===0)"

      +"    postMessage(i);"

      +"postMessage('Done');");

  console.log(workertext);

  let w1=new Worker(workertext);

  let w2=new Worker(workertext);

  w1.onmessage=m=>log1.innerText+=" "+m.data;

  w2.onmessage=m=>log2.innerText+=" "+m.data;

}

<div id="log1">Worker1: </div>

<div id="log2">Worker2: </div>

旁注:有趣的是,在超出带符号的 32 位整数范围(日志中的 2100000000 -> 2200000000,或实际边界为 2147483647 -> 2147483648)后,计数会变慢。至少在我的 Chrome 中。


查看完整回答
反对 回复 2022-01-07
  • 2 回答
  • 0 关注
  • 227 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号