为什么JS承诺先打印所有的解析然后拒绝第二个 [英] Why does JS promise print all the resolve first then rejects second

查看:77
本文介绍了为什么JS承诺先打印所有的解析然后拒绝第二个的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么承诺首先打印所有成功然后是拒绝后,即使我为其编写的代码随机出现

Why does promise print all the success first then the rejects after, even though i wrote the code for it to appear randomly

var errors = 0;
var successes = 0;
var p1;
for (var i = 0; i < 10; i++) {
  p1 = new Promise(function(resolve, reject) {
    var num = Math.random();
    if (num < .5) {
      resolve(num);
    } else {
      reject(num)
    }
  });

  p1.then(function success(res) {
    successes++;
    console.log("*Success:* " + res)
  }).catch(function error(error) {
    errors++
    console.log("*Error:* " + error)
  });
}

输出

VM331:16 *Success:* 0.28122982053146894
VM331:16 *Success:* 0.30950619874924445
VM331:16 *Success:* 0.4631742111936423
VM331:16 *Success:* 0.059198322061176256
VM331:16 *Success:* 0.17961879374514966
VM331:16 *Success:* 0.24027158041021068
VM331:19 *Error:* 0.9116586303879894
VM331:19 *Error:* 0.7676575145407345
VM331:19 *Error:* 0.5289135948801782
VM331:19 *Error:* 0.5581542856881132


推荐答案

它与异步代码的工作方式有关

It has to do with how asynchronous code works

.then()。catch() - 必须等待队列两次(嗯,我需要解释一下)

.then().catch() - has to wait for the queue twice (hmm, I need to explain this)

.then()只有一次

承诺本质上是ynchronous ...在你的代码中,当一个promise解析时, .then 代码放在微任务上?队列...并依次处理

Promises are by nature asynchronous ... in your code, when a promise resolves, the .then code is put on the microtask? queue ... and processed in turn

拒绝时,因为 .then 没有 onRejected 回调,那么,你的案例中的承诺链 .catch 中的下一个处理程序是否被添加到微任务中?队列 - 但到那时,所有 .then 代码都已执行

when it rejects, as .then has no onRejected callback, so, the next handler in the promise chain .catch in your case is added to the microtask? queue - but by then, all the .then codes have been executed

尝试使用 .then(onSuccess,onError),你会得到你期望的结果

try using .then(onSuccess, onError) and you'll get what you expect

var errors = 0;
var successes = 0;
var p1;
for (var i = 0; i < 10; i++) {
    p1 = new Promise(function(resolve, reject) {
        var num = Math.random();
        if (num < .5) {
            resolve(num);
        } else {
            reject(num);
        }
    });
    p1.then(function success(res) {
        successes++;
        console.log("*Success:* " + res);
    }, function error(error) {
        errors++;
        console.log("*Error:* " + error);
    });
}

另一种方式(至少在本机Promises中)得到你所追求的是

Another way (at least in native Promises) to get what you are after is

var errors = 0;
var successes = 0;
var p1;
for (let i = 0; i < 10; i++) {
    p1 = new Promise(function(resolve, reject) {
      setTimeout(() => {
            var num = Math.random();
            if (num < .5) {
                resolve(`${i} ${num}`);
            } else {
                reject(`${i} ${num}`)
            }
        });
    });
    p1.then(function success(res) {
        successes++;
        console.log("*Success:* " + res)
    }).catch(function error(error) {
        errors++
        console.log("*  Error:* " + error)
    });
}

这是因为setTimeout延迟了解决/拒绝

This is because the setTimeout delays the resolve/reject


深入解释


首先要做的事情......你需要明白 .then 实际上是

First things first ... you need to understand that .then is actually

.then(onFullfilled, onRejected)

并返回Promise

and returns a Promise

接着, .catch 只是语法糖

.then(null, onRejected)

事实上,在大多数Promise库中(在它们出生之前)它被定义为

in fact, in most Promise libraries (before they went native) it is defined as

Promise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected);
};

正确...所以让我们来看看你的代码的简单版本 - 只有使用三个承诺简洁

Right ... so let's look at an un-wound, simple version of your code - and only use THREE promises for brevity

function executorFunction(resolve, reject) {
    const num = Math.random();
    if (num < .5) {
      resolve(num);
    } else {
      reject(num)
    }
}
let successes = 0, errors = 0;
function success(res) {
    successes++;
    console.log("*Success:* " + res)
}
function error(error) {
    errors++
    console.log("*Error:* " + error)
}

const p1 = new Promise(executorFunction);
p1.then(success).catch(error);

const p2 = new Promise(executorFunction);
p2.then(success).catch(error);

const p3 = new Promise(executorFunction);
p3.then(success).catch(error);

你可以运行它并看到它产生相同的成功顺序和错误

You can run that and see that it produces the same order of success and error

现在,让我们稍微改变一下,这样我们总能获得成功/失败/成功

Now, let's change it up a bit, so we always get success/fail/success

function executorFunction(num, fail) {
    return (resolve, reject) => {
        if (fail) {
          reject(num);
        } else {
          resolve(num)
        }
    };
}
function success(res) {
    console.log("*Success:* " + res)
}
function error(error) {
    console.log("*Error:* " + error)
}

const p1 = new Promise(executorFunction(1, false));
p1.then(success).catch(error);

const p2 = new Promise(executorFunction(2, true));
p2.then(success).catch(error);

const p3 = new Promise(executorFunction(3, false));
p3.then(success).catch(error);

这总是输出

*Success:* 1
*Success:* 3
*Error:* 2

所以我们看到订单你在问题中看到了 - 到目前为止很好

So we see the order you are seeing in your question - so far so good

现在,让我们用扩展形式重写.then / .catch

Now, let's rewrite the .then/.catch in there expanded form

function executorFunction(num, fail) {
    return (resolve, reject) => {
        if (fail) {
          reject(num);
        } else {
          resolve(num)
        }
    };
}
function success(res) {
    console.log("*Success:* " + res)
}
function error(error) {
    console.log("*Error:* " + error)
}

const p1 = new Promise(executorFunction(1, true));
p1.then(success, null).then(null, error);

const p2 = new Promise(executorFunction(2, false));
p2.then(success, null).then(null, error);

仅限使用两个承诺,FIRST拒绝...我们知道这将输出成功2 然后错误1 - 即我们期待的相反顺序

let's only use two promises, the FIRST rejecting ... we know that this will output success 2 then error 1 - i.e. in the reverse order we are expecting

所以让我们分析一下发生了什么

So let's analyse what is happening

因为你正在同步解析/拒绝在Promise构造函数中 executorFunction

Because you're resolving/rejecting synchronously in the Promise constructor executorFunction

const p1 = new Promise(executorFunction(1, false));

是一个已解决的Promise - 对于p2已经达到2,并且对于p1被拒绝,原因为1,但是它永远不会处于待定状态。因此,当一个承诺没有待处理(它已经解决,但这意味着要么已经完成或被拒绝,但是术语已经混淆了,所以我会继续说没有待定),任何处理程序都被添加到微任务队列 - 所以在所有代码结束时,微任务队列看起来像

is immediatelty a resolved Promise - fulfilled as 2 for p2, and rejected with reason 1 for p1, but it's never in a Pending state. So, when a promise is not pending (it's resolved, but that means either fulfilled or rejected, but the terminology has been mixed up, so I'll keep saying "not pending"), any "handler" is added to the microtask queue - so at the end of all that code, the microtask queue looks like

**microtask queue**
(resolved:2).then(success, null).then(null, error); // added second
(rejected:1).then(success, null).then(null, error); // added first

现在JS引擎,因为没有任何运行,处理微任务队列(顺便说一句,队长是在底部)

Now the JS engine, since there's nothing running anymore, processes the microtask queue (the head of the queue is at the bottom by the way)


  • 它看到被拒绝的承诺(1),但是 .then 没有被拒绝的功能,所以承诺值带有链

  • .then 使用原始拒绝原因返回此被拒绝的承诺

  • 此承诺,因为它有一个处理程序(原始代码中的.catch)被添加到微任务队列

  • it sees a rejected promise(1), but the .then has no on rejected function, so the promise value carries down the chain
  • .then returns this rejected promise with the original rejection reason
  • this promise, because it has a handler (.catch in the original code) is added to the microtask queue

**microtask queue**
(rejected:1)>(rejected:1).then(null, error);         // added third
(resolved:2).then(success, error).then(null, error); // added second

现在处理下一个微任务


  • 它看到已解决的承诺(2)所以调用成功

  • 输出成功2

  • .then 返回一个承诺,因为你的成功函数没有返回,这是返回undefined 并且承诺被解析为 undefined

  • 这个承诺,因为它有一个处理程序(原始代码中的.catch)被添加到微任务队列

  • It sees a resolved promise(2) so calls success
  • outputs success 2
  • .then returns a promise, because your success function has no return, this is return undefined and the promise is resolved as undefined
  • this promise, because it has a handler (.catch in the original code) is added to the microtask queue

**microtask queue**
(resolved:2)>(resolved:undefined).then(null, error); // added fourth
(rejected:1)>(rejected:1).then(null, error);         // added third




  • 它看到被拒绝的承诺(1)并且那里是一个onrejected处理程序调用错误

  • 输出错误1

  • .then 返回一个承诺,没有处理程序,因此没有任何内容添加到微任务队列

    • It sees a rejected promise(1) and there is an onrejected handler calls error
    • outputs error 1
    • .then returns a promise, there's no handler, so nothing is added to the microtask queue
    • **microtask queue**
      (resolved:2)>(resolved:undefined).then(null, error); // added fourth
      

      现在处理下一个微任务


      • 它看到已解决的承诺(2,现在未定义) - 但没有onSuccess处理程序

      • .then 返回一个promise,没有处理程序,因此没有任何东西被添加到微任务队列

      • It sees a resolved promise(2, now undefined) - but there is no onSuccess handler
      • .then returns a promise, there's no handler, so nothing is added to the microtask queue

      **microtask queue**
      **empty**
      








      为什么使用.then(onFullfilled,onRejected)会产生预期的订单


      好的,现在,如果我们写代码

      OK, so now, if we write the code

      function executorFunction(num, fail) {
          return (resolve, reject) => {
              if (fail) {
                reject(num);
              } else {
                resolve(num)
              }
          };
      }
      function success(res) {
          console.log("*Success:* " + res)
      }
      function error(error) {
          console.log("*Error:* " + error)
      }
      const p1 = new Promise(executorFunction(1, true));
      p1.then(success, error);
      
      const p2 = new Promise(executorFunction(2, false));
      p2.then(success, error);

      微任务队列开始,如

      **microtask queue**
      (resolved:2).then(success, error); // added second
      (rejected:1).then(success, error); // added first
      

      现在处理下一个微任务


      • 它看到被拒绝的承诺(1)所以调用错误

      • 输出错误1

      • .then 返回承诺,有没有处理程序,所以没有任何东西被添加到微任务队列

      • It sees a rejected promise(1) so calls error
      • outputs error 1
      • .then returns a promise, there's no handler, so nothing is added to the microtask queue

      **microtask queue**
      (resolved:2).then(success, error); // added second
      




      • 它看到已解决的承诺(2)所以调用成功

      • 输出成功2

      • .then 返回一个promise,没有处理程序,因此没有任何内容添加到微任务队列

        • It sees a resolved promise(2) so calls success
        • outputs success 2
        • .then returns a promise, there's no handler, so nothing is added to the microtask queue
        • **microtask queue**
          **empty**
          








          为什么将setTimeout结果添加到预期的顺序


          现在让我们更改executorFunction以添加setTimeout

          Now let's change the executorFunction to add the setTimeout

          function executorFunction(num, fail) {
              return (resolve, reject) => {
                  setTimeout(function() {
                      if (fail) {
                        reject(num);
                      } else {
                        resolve(num)
                      }
                  });
              };
          }
          function success(res) {
              console.log("*Success:* " + res)
          }
          function error(error) {
              console.log("*Error:* " + error)
          }
          
          const p1 = new Promise(executorFunction(1, true));
          p1.then(success, null).then(null, error);
          
          const p2 = new Promise(executorFunction(2, false));
          p2.then(success, null).then(null, error);

          再次,为简洁起见,我们只使用两个承诺,第一个失败,因为我们知道原始代码中的输出将是成功2 然后失败1
          现在我们有两个队列要考虑...... microtask和timer - 定时器队列的优先级低于微任务队列...即当没有运行(立即)时,JS将处理微任务队列,直到它在尝试计时器队列之前是空的

          Again, for brevity, let's use only TWO promises, and the first one fails, because we know that the output in the original code would be success 2 then fail 1 Now we have two queues to consider ... microtask and "timer" - the timer queue has lower priority than the microtask queue ... i.e. when there's nothin running (immediately) then JS will process the microtask queue until it is empty, before even trying the timer queue

          所以 - 这时我们去吧

          So - here we go then

          结束时我们的代码

          ** empty microtask queue **                             timer queue
                                                                  setTimeout(resolve(2))
                                                                  setTimeout(reject(1))
          

          处理计时器队列,我们​​得到一个微任务(被拒绝:1)。然后(成功,空).then(null,错误)

          Processing the timer queue, we get a microtask (rejected:1).then(success, null).then(null, error)

          ** microtask queue **                                   timer queue
          (rejected:1).then(success, null).then(null, error)      setTimeout(resolve(2))
          

          哦,微任务队列中有一些东西,让我们处理并忽略计时器队列

          Oh, there's something in the microtask queue, lets process that and ignore the timer queue


          • 它看到被拒绝的承诺(1),但 .then 没有被拒绝的功能,因此承诺价值带有链条

          • .then 以原始拒绝原因返回此被拒绝的承诺

          • 这个承诺,因为它有一个处理程序(原始代码中的.catch)被添加到微任务队列

          • it sees a rejected promise(1), but the .then has no on rejected function, so the promise value carries down the chain
          • .then returns this rejected promise with the original rejection reason
          • this promise, because it has a handler (.catch in the original code) is added to the microtask queue

          哦,微任务队列中有一些东西,让我们处理并忽略计时器队列

          Oh, there's something in the microtask queue, lets process that and ignore the timer queue

          ** microtask queue **                                   timer queue
          (rejected:1).then(success, null).then(null, error)      setTimeout(resolve(2))
          




          • 它看到被拒绝的承诺(1)并且有一个onrejected处理程序调用错误

          • 输出错误1

          • .then 返回一个promise,没有处理程序,因此没有任何东西被添加到microtask队列中

            • It sees a rejected promise(1) and there is an onrejected handler calls error
            • outputs error 1
            • .then returns a promise, there's no handler, so nothing is added to the microtask queue
            • 现在队列看起来像

              ** microtask queue **                                   timer queue
                                                                      setTimeout(resolve(2))
              

              因此,我不需要继续,因为错误1 已在第二个承诺链开始之前输出:p1

              So, I don't need to go on, right, because error 1 has been output before the second promise chain has even started :p1

              这篇关于为什么JS承诺先打印所有的解析然后拒绝第二个的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆