Promise

Promise的三种状态

 (1). 待定(pending): 初始状态,既没有被兑现,也没有被拒绝;当执行executor中的代码时,处于该状态;

 (2). 已兑现(fulfilled): 意味着操作成功完成;执行了resolve时,处于该状态; (这个状态通常也叫做 resolved,更容易被记住)

 (3). 已拒绝(rejected): 意味着操作失败;执行了reject时,处于该状态;

 注:一旦状态被确定下来,Promise的状态会被 锁死,该Promise的状态是不可更改的。

在我们调用resolve的时候,如果resolve传入的值本身不是一个Promise,那么会将该Promise的状态变成 兑现(fulfilled); 在之后我们去调用reject时,已经不会有任何的响应了(并不是这行代码不会执行,而是无法改变Promise状态)

js
1{
2    console.log("----------3. Promise的三种状态----------");
3    /* const myPromise = new Promise((resolve, reject) => {
4        resolve("333");
5    });
6    myPromise.then(res => {}).catch(err => {}); */
7    // 下面代码是立即执行的,等价于上面代码
8    new Promise((resolve, reject) => {
9        // 状态1 该进来的时候是 pending状态, 待定状态
10        console.log("------------------------");
11        resolve(11); //状态2 fulfilled 成功状态
12        reject(22); //状态3  rejected 失败状态 (注:不生效了,因为前面resolve已经确定状态了,就锁定了,不能再改了)
13    })
14        .then(res => {
15            console.log(res);
16        })
17        .catch(err => {
18            console.log(err);
19        });
20}

resolve 参数

(1). 如果resolve传入一个普通的值或者对象,那么这个值会作为then回调的参数。

(2). 如果resolve中传入的是另外一个Promise,那么这个新Promise会决定原外层Promise的状态.

      A. 内层Promise调用resolve(fulfilled状态),那么就进入then中的回调成功的位置

      B. 内层Promise调用reject(rejected状态),那么就进入then中回调失败的位置  或者  catch 中

(3). resolve中传入的是一个对象,并且这个对象有实现then方法,那么会执行该then方法,并且根据then方法的结果来决定Promise的状态

       A. then中调用resolve,那么最终进入then中的回调成功的位置

       B. then中调用reject,那么最终就进入then中回调失败的位置  或者  catch 中

js
1// 4.1 传入普通值
2{
3    console.log("--------------4.1 传入普通值------------------");
4    const promiseResult = new Promise((resolve, reject) => {
5        resolve("ypf1");
6    });
7    promiseResult.then(res => console.log(res)); //ypf1
8}
9// 4.2 传入Promise
10{
11    console.log("-------------- 4.2 传入Promise------------------");
12    const promiseResult = new Promise((resolve, reject) => {
13        resolve(
14            new Promise((resolve2, reject2) => {
15                reject2("ypf2");
16            })
17        );
18    });
19    promiseResult.then(null, err => console.log(err)); //ypf2
20}
21
22// 4.3 传入对象,且对象有实现then方法
23{
24    console.log("------4.3 传入对象,且对象有实现then方法-----------");
25    const promiseResult = new Promise((resolve, reject) => {
26        resolve({
27            then: function (resolve2, reject2) {
28                reject2("ypf3");
29            },
30        });
31    });
32    promiseResult.then(null, err => console.log(err)); //ypf3
33}

Promise 的构造函数

new Promise() 的过程是同步的,如下代码的执行顺序:

js
1new Promise(() => {
2  console.log('new Promise')
3})
4
5console.log('after new Promise')
6// new Promise
7// after new Promise

对象方法

then

(1). then方法接收两个参数:

      A.fulfilled的回调函数:当状态变成fulfilled时会回调的函数;

      B.reject的回调函数:当状态变成reject时会回调的函数;

注:reject的回调函数等价于catch方法中调用

(2). then方法本身也是有返回值的, 它的返回值是Promise.

  A. 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个新的Promise的resolve值

  B. 如果我们返回的是一个Promise,那么当前的Promise的状态会由传入的Promise的状态来决定 相当于状态进行了移交 【原理同 ‘resolve参数’】

  C. 如果返回的是一个对象, 并且该对象实现了then方法,则由then方法中的状态进行决定哦 【原理同 ‘resolve参数’】

  D. 当then方法抛出一个异常时,那么它处于reject状态;

catch

catch 也是支持链式吊用的。

js
1{
2    console.log("-------2.3 catch的返回值------------");
3    const promise = new Promise((resolve, reject) => {
4        reject("ypf1");
5    });
6
7    promise
8        .then(res => {
9            console.log("res:", res);
10        })
11        .catch(err => {
12            console.log("err:", err); //先执行这句话 【err: ypf1】
13            return "ypf2";
14        })
15        .then(res => {
16            console.log("res result:", res); //再执行这句话 【res result: ypf2】
17        })
18        .catch(err => {
19            console.log("err result:", err);
20        });
21}

finally

静态方法

resolve

reject

all

(1). 它的作用是将多个Promise包裹在一起形成一个新的Promise;

(2). 新的Promise状态由包裹的所有Promise共同决定:

      A. 当所有的Promise状态变成fulfilled状态时(resolved),新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组;

      B. 当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数;

allSettled

(1). 背景

  all方法有一个缺陷:当有其中一个Promise变成reject状态时,新Promise就会立即变成对应的reject状态。

   那么对于resolved的,以及依然处于pending状态的Promise,我们是获取不到对应的结果的;

(2). 在ES11(ES2020)中,添加了新的API Promise.allSettled:该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是reject时,才会有最终的状态

       并且这个Promise的结果一定是fulfilled的;

(3).返回值:

      allSettled的结果是一个数组,数组中存放着每一个Promise的结果,并且是对应一个对象的;这个对象中包含status状态,以及对应的value值;

      console.log(item.status, item.value, item.reason);

      A.statue:表示状态, fulfilled 或 rejected

      B.value: fulfilled时对应的内容

      C.reason: reject时对应的内容

race

如果有一个Promise有了结果,我们就希望决定最终新Promise的状态,那么可以使用race方法:

any

any方法是ES12中新增的方法,和race方法是类似的:

(1). any方法会等到一个fulfilled状态,才会决定新Promise的状态;

(2). 如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态;

注: 如果所有的Promise都是reject的,那么会报一个AggregateError的错误。

Promise 的实现

js
1class Promise {
2    constructor(executor) {
3        this.status = 'PENDING'; // 初始化状态声明 PENDING RESOLVED REJECTED
4        this.data = null; // 成功值
5        this.reason = null; // 失败值
6        this.resolvecbQueue = []; // 成功回调队列
7        this.rejectcbQueue = []; // 失败回调队列
8
9        executor(this.resolve.bind(this), this.reject.bind(this));
10    }
11
12    resolve(data) {
13        // 负责执行
14        if (this.status === 'PENDING') {
15            this.status = 'RESOLVED';
16            this.data = data;
17            // 执行回调
18            this.resolvecbQueue.forEach((itemFn) => {
19                itemFn(data);
20            });
21        }
22    }
23
24    reject(reason) {
25        if (this.status === 'PENDING') {
26            this.status = 'REJECTED';
27            this.reason = reason;
28            // 执行回调
29            this.rejectcbQueue.forEach((itemFn) => {
30                itemFn(reason);
31            });
32        }
33    }
34
35    // then 一定是要返回 Promise, 两个传进来的参数是成功回调, 失败回调
36    // promise1.then(onResolved, onRejected)
37    then(onResolvedFn, onRejectedFn) {
38        // 类型判断, 如果不是function类型, 则让它是个function
39        onResolvedFn = typeof onResolvedFn === 'function' ?  onResolvedFn : function (value) {};
40        onRejectedFn = typeof onRejectedFn === 'function' ?  onRejectedFn : function (reason) {};
41        
42        // 在注册阶段执行
43        if (this.status === 'PENDING') {
44            return new Promise((resolve, reject) => {
45                // 注册阶段 我们并不确定是调用哪个类型, 所以需要将两种情况都放入到队列里
46                this.resolvecbQueue.push(value => {
47                    const returndata = onResolvedFn(value); // 执行的时候需要做判断
48                    if (returndata instanceof Promise) {
49                        returndata.then(resolve, reject);
50                    } else {
51                        resolve(value);
52                    }
53                });
54                // 同理
55                // this.rejectcbQueue.push(reason => onRejectedFn(reason));
56            });
57        }
58
59        // 说明是要返回Promise
60        if (this.status === 'RESOLVED') {
61            return new Promise((resolve, reject) => {
62                // eg: .then(value => value, () => {})
63                // 需要判断onResolvedFn 返回值 Promise类型, 没有则直接 resolve
64                const returndata = onResolvedFn(this.data);
65                // 需要继续then下去
66                if (returndata instanceof Promise) {
67                    // 继续执行
68                    returndata.then(resolve, reject);
69                } else {
70                    resolve(this.data);
71                }
72            });
73        }
74
75        if (this.status === 'REJECTED') {
76            return new Promise((resolve, reject) => {
77                const returndata = onRejectedFn(this.data);
78                if (returndata instanceof Promise) {
79                    returndata.then(resolve, reject);
80                } else {
81                    reject(this.value);
82                }
83            });
84        }
85    }
86}

关于 postMessage

https://stackoverflow.com/questions/60694838/js-web-worker-is-a-macro-task

Concretely this does mean that if you schedule two asynchronous tasks from the same event loop, the message event should win, even though it's been called after:

js
1setTimeout( () => console.log( 'timeout' ), 0 );
2onmessage = e => console.log( 'message' );
3postMessage( '', '*' );
4// timeout 
5// message