0

我通过 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));
}

这两个代码有什么区别?

附录

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

1.
const parallel = () => {
    promiseFunction1()
        .then(res => console.log(res));
    promiseFunction2()
        .then(res => console.log(res));
}

2.
const parallel = () => {
    promiseFunction1()
        .then(res1 => {
            console.log(res1);
            promiseFunction2()
                .then(res2 => {
                    console.log(res2);
                })
        });
}
4

2 回答 2

0

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

Promise首先promiseFunction1立即解析,因此它首先进入微任务队列,因此它的关联.then首先运行。

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

对于您的第一个代码:

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

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

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

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

于 2019-11-22T07:41:02.057 回答
0

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>

而且拥有多个独立Promise的s并不代表它们一起运行,而是它们一起等待
当然,这种情况下也有一些不错的辅助工具。就像等待许多事件,并在所有事件都完成后做某事可能看起来像一些讨厌的、命名的回调加上一些更讨厌的计数器:

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 中。

于 2019-11-22T11:31:14.743 回答