avatar
fireworks99
keep hungry keep foolish

JavaScript Note 2022.09.02

关于JavaScript中的Promise的认识

1.Promise的诞生

Promise是ES6新增的一个类,能够让开发者更优雅地写复杂的异步任务

下面是一个层层递进的异步任务:

setTimeout(function () {
    console.log("1 minute later");
    setTimeout(function () {
        console.log("1+3 minutes later");
        setTimeout(function () {
            console.log("1+3+5 minutes later");
        }, 5000);
    }, 3000);
}, 1000);

上述代码是一种嵌套格式,深层缩进,既不美观又难以阅读。

于是为解决这一问题,Promise应运而生。

new Promise(function (resolve) {
  setTimeout(function () {
    console.log("1 second later");
    resolve();
  }, 1000);
}).then(function () {
  setTimeout(function () {
    console.log("2 second later");
  }, 1000);
}).then(function () {
  setTimeout(function () {
    console.log("3 second later");
  }, 1000);
})

上述代码是一种顺序格式,浅层缩进,美观易阅读。

2.Promise的内部属性

new Promise 构造器返回的 promise 对象具有以下内部属性:

  • state —— 最初是 "pending",然后在 resolve 被调用时变为 "fulfilled/resolved",或者在 reject 被调用时变为 "rejected"
  • result —— 最初是 undefined,然后在 resolve(value) 被调用时变为 value,或者在 reject(error) 被调用时变为 error

Promise 对象的 stateresult 属性都是内部的。我们无法直接访问它们。但我们可以对它们使用 .then/.catch/.finally 方法。

①.state

Promise有两种statependingsettled,其中settled又包含fulfilledrejected

promise state

②.result

Promise有三种result:undefined、value、error。

promise_result

补充:

  • Promise构造函数中,不调用resolve与reject,则首个then不会执行。
  • Promise构造函数中,只调用二者之一便执行那个函数。
  • Promise构造函数中,两者都调用,只执行首个被调用的
    • 原因:调用首个之后Promise的状态被更改了,任何状态的更改都是最终的

3.Promise的构造函数

let promise = new Promise(function(resolve, reject) {
    setTimeout(function () {
        //doSth...
        resolve("1 second later.");
    }, 1000);
}).then(function(value) {
    console.log(value);
}, function(error) {
    console.log(error);
})

Promise构造函数只有一个参数,是一个函数,被称为executor(执行器 / 起始函数)。

resolve/reject 只需要一个参数(或不包含任何参数),并且将忽略额外的参数

补充(执行器立马执行):

let p = new Promise((resolve, reject) => {
  console.log(1);
  resolve();
})
p.then(() => console.log(3))

console.log(2);

new Promise()的时候,Promise的执行器就会立马执行!
但是调用resolve()会触发异步操作,传入的then()方法的函数会被添加到 任务队列 并异步执行。
且这里的任务队列指的是“微任务队列”(Microtask queue)。

上述代码输出顺序为1 -> 2 -> 3。

补充(执行器参数名可任意取):

let p = new Promise(function (a, b) {
  setTimeout(function () {
    b("Hello");                         //6 error: Hello
  },1000);

}).then(function (result) {
  console.log("6 result: " + result);
}, function (error) {
  console.log("6 error: " + error);
})


let q = new Promise(function (reject, resolve) {
  setTimeout(function () {
    resolve("Hello");                  //6 error: Hello
  },1000);

}).then(function (result) {
  console.log("6 result: " + result);
}, function (error) {
  console.log("6 error: " + error);
})

执行器函数 参数的名字和实际调用的 resolve() 和 reject() 无关,关键在于顺序

第一个参数对应then中第一个函数

第二个参数对应then中第二个函数

4.Promise的三个方法

Promise 类有 .then() .catch().finally() 三个方法 。

new Promise(function (resolve, reject) {
    var a = 0;
    var b = 1;
    if (b == 0) reject("Divide zero");
    else resolve(a / b);
}).then(function (value) {
    console.log("a / b = " + value);
}).catch(function (err) {
    console.log(err);
}).finally(function () {
    console.log("End");
});

a / b = 0

End

.catch(func) 调用 等效于.then(null, func)

补充(then、catch、finally是有顺序的):

new Promise((resolve, reject) => {
  setTimeout(() => resolve("value"), 2000)
})
  .finally(() => alert("Promise ready")) // 先触发
  .then(result => alert(result)); // <-- .then 显示 "value"

Promise ready

value

这里finally早于then执行,毕竟 finally 并不意味着处理一个 promise 的结果。无论结果是什么,它都是进行常规清理的地方。

5.Promise vs callback

Promise callback
代码逻辑上 顺其自然:拿到结果再思考如何处理 尚未开始就得先考虑好拿到结果后如何处理
多层异步时 顺序格式:缩进简单,赏心悦目 嵌套格式:层层缩进,混乱无序

6.Promise chain

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000); // (*)

}).then(function(result) { // (**)

  alert(result); // 1
  return result * 2;

}).then(function(result) { // (***)

  alert(result); // 2
  return result * 2;

}).then(function(result) {

  alert(result); // 4
  return result * 2;

});

chain 1

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 1000);
});

promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});

promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});

promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});

chain 2

new Promise()中有resolvereject将promise改为fulfilled状态或rejected状态,首个then才会被触发。首个then返回一个promise对象,第二个then是这个promise对象的“首个then”。而第二个then以及后续then也能够被触发说明返回的这个promise对象并非pending状态,而是fulfilled状态或rejected状态。

Promise.prototype.then()的返回值:

当一个Promise完成(fulfilled)或者失败(rejected)时,返回函数将被异步调用(由当前的线程循环来调度完成)。具体的返回值依据以下规则返回。如果 then 中的回调函数:

  • 返回了一个值,那么 then 返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
  • 没有返回任何值,那么 then 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined
  • 抛出一个错误,那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
  • 返回一个已经是接受状态的 Promise,那么 then 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的 Promise 的接受状态回调函数的参数值。
  • 返回一个已经是拒绝状态的 Promise,那么 then 返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的 Promise 的拒绝状态回调函数的参数值。
  • 返回一个未定状态(pending)的 Promise,那么 then 返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。

7.Promise.all()

let username = undefined;
let flag = false;

{
  //替代Promise.all()的方案
  setTimeout(() => {
    username = "admin";
    doSth();
  }, 1000);
  setTimeout(() => {
    flag = true;
    doSth();
  }, 2000);

  function doSth() {
    if(username && flag) {
      console.log("All is will.");
      console.log(username);
      console.log(flag);
      //doSth
    }
  }
}

Promise.all([
    new Promise((resolve) => {
      setTimeout(() => {
        username = "admin";
        resolve();
      }, 1000);
    }),
    new Promise((resolve) => {
      setTimeout(() => {
        setTimeout(() => {
          flag = true;
          resolve();
        })
      }, 2000);
    })
]).then(() => {
  console.log("All is will");
  console.log(username);
  console.log(flag);
})

参考文档

  1. Runoob,JavaScript Promise,https://www.runoob.com/js/js-promise.html
  2. JAVASCRIPT.INFO,Promise,https://zh.javascript.info/promise-basics
  3. JAVASCRIPT.INFO,Promise chain,https://zh.javascript.info/promise-chaining
  4. MDN Web Docs,Promise,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
  5. MDN Web Docs, Promise.prototype.the(),https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
Site by Baole Zhao | Powered by Hexo | theme PreciousJoy