javascript – 为什么JS承诺先打印所有解析然后拒绝第二个

前端之家收集整理的这篇文章主要介绍了javascript – 为什么JS承诺先打印所有解析然后拒绝第二个前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
为什么承诺首先打印所有成功然后是拒绝后,即使我为其编写代码随机出现
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)
  });
}

OUTPUT

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

解决方法

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

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

.then()只有一次

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

当它拒绝时,因为.then没有onRejected回调,那么,你的情况下的promise链.catch中的下一个处理程序是否被添加到microtask中?队列 – 但到那时,所有.then代码都已执行

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

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中)是

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会延迟解析/拒绝

An in-depth explanation

首先要做的事情……你需要了解它.然后才是

.then(onFullfilled,onRejected)

并返回一个Promise

接下来,.catch只是“语法糖”

.then(null,onRejected)

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

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

是的……所以让我们来看看你的代码的一个简单的简单版本 – 为了简洁,只使用三个承诺

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);

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

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

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

所以我们看到你在问题中看到的顺序 – 到目前为止一切顺利

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

function executorFunction(num,true));
p1.then(success,null).then(null,error);

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

让我们只使用两个承诺,FIRST拒绝…我们知道这将输出成功2然后错误1 – 即以我们期望的相反顺序

那么让我们来分析一下发生了什么

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

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

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

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

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

>它看到一个被拒绝的承诺(1),但.then没有被拒绝的函数,所以承诺值继承了链
>.然后返回此拒绝的承诺与原始拒绝原因
>这个承诺,因为它有一个处理程序(原始代码中的.catch)被添加到微任务队列中

.

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

现在处理下一个微任务

>它看到了一个已解决的承诺(2)所以要求成功
>输出成功2
> .then返回一个promise,因为你的success函数没有返回,这是return undefined,promise被解析为undefined
>这个承诺,因为它有一个处理程序(原始代码中的.catch)被添加到微任务队列中

.

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

>它看到一个被拒绝的promise(1)并且有一个onrejected处理程序调用错误
>输出错误1
> .then返回一个promise,没有处理程序,因此没有任何东西被添加到微任务队列中

.

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

现在处理下一个微任务

>它看到一个已解决的承诺(2,现在未定义) – 但没有onSuccess处理程序
> .then返回一个promise,因此没有任何东西被添加到微任务队列中

.

**microtask queue**
**empty**

why using .then(onFullfilled,onRejected) results in the expected order

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

function executorFunction(num,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,error);

微任务队列开始,就像

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

现在处理下一个微任务

>它看到被拒绝的承诺(1)所以调用错误
>输出错误1
> .then返回一个promise,因此没有任何东西被添加到微任务队列中

.

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

>它看到了一个已解决的承诺(2)所以要求成功
>输出成功2
> .then返回一个promise,因此没有任何东西被添加到微任务队列中

.

**microtask queue**
**empty**

why adding setTimeout results in the expected order

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

function executorFunction(num,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,error);

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

所以 – 我们走了

在我们的代码结束时

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

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

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

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

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

哦,让我们处理它并忽略定时器队列

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

>它看到一个被拒绝的promise(1)并且有一个onrejected处理程序调用错误
>输出错误1
> .then返回一个promise,因此没有任何东西被添加到微任务队列中

现在队列看起来像

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

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

原文链接:https://www.f2er.com/js/152991.html

猜你在找的JavaScript相关文章