笔记3--promise

108 阅读9分钟

课程目标

  1. promise 规范
  2. promise 实现
  3. promise 使用

PromiseA+规范

术语

  1. promise 是一个有then方法的对象或函数,行为遵循本鬼发
  2. thenable 是一个有then方法的对象或函数
  3. value 是promise状态成功时的值,也是resolve的参数;resolve参数包括各种数据类型:undefined、thenable、promise
  4. reason 是promise状态失败时的值,也是reject的参数,表示拒绝(失败)的原因
  5. exception 是一个使用throw抛出的异常值

规范

Promise States(Promise 状态)

  1. pending

    1. 初始的状态,可以改变
    2. 一个promise resolve/reject前,都处于这个状态‘
    3. 可以通过resolve 变成 【fulfilled】状态
    4. 可以通过reject 变成 【rejected】状态
  2. fulfilled

    1. 最终状态,不可改变
    2. 一个promise 被resolve后,变成此状态
    3. 必须拥有一个value值
  3. rejected

    1. 最终状态,不可改变
    2. 一个promise 被reject后,变成此状态
    3. 必须拥有一个reason值

pending → resolve(value) → fulfilled

pending → reject(reason) → rejected

then

Promise 应当提供一个then方法,用于访问最终的结果,无论是value还是reason

promise.then(onFulfilled, onRejected)
  1. 参数要求

    1. onFulfilled 必须是函数类型,如果不是,应当被忽略
    2. onRejected 必须是函数类型,如果不是,应当被忽略
  2. onFulfilled 特性

    1. promise状态变成fulfilled时,应调用onFulfilled,参数为value
    2. promise变成fulfilled状态前,不应该被调用
    3. 只能调用一次(实现时,应当限制执行次数)
  3. onRejected 特性

    1. promise状态变成rejected时,应调用onRejected,参数为reason
    2. promise变成rejected状态前,不应该被调用
    3. 只能调用一次(实现时,应当限制执行次数)
  4. onFulfilled 和 onRejected 应是微任务

    使用queueMicrotask实现微任务调用

  5. then方法可以被调用多次

    1. promise 变成 fulfilled状态后,所有onFulfilled 回调都需要按照then的顺序执行,即按照then的注册顺序执行(实现时,需要一个数组,存放onFulfilled回调)
    2. promise 变成 rejected状态后,所有onFulfilled 回调都需要按照then的顺序执行,即按照then的注册顺序执行(实现时,需要一个数组,存放onRejected回调)
  6. 返回值

then 应该返回一个promise

promise2 = promise1.then(onFulfilled, onRejected);
  1. onFulfilled 或 onRejected执行的结果为 x,调用resolvePromise
  2. 若 onFulfilled 或 onRejected执行时,抛出异常e,promise2需要被reject
  3. 若 onFulfilled 不是一个函数,promise2以promise1的value触发fulfilled
  4. 若 onRejected 不是一个函数,promise2以promise1的reason触发rejected
  5. resolvePromise
resolvePromise(promise2, x, resolve, reject);
  1. 若 promise2 与 x 相等,reject TypeError

  2. 若 x 是一个promise

    1. 若 x 是 pending 状态,那么promise必须要继续pending,直到x变成fulfilled 或rejected
    2. 若 x 是 fulfilled 状态,resolve promise with the same value
    3. 若 x 是 rejected 状态,reject promise with the same reason

c. 若 x 是一个objet 或function

let then = x.then;
// 若这一步出错,reject promise with e as the reason

// 若then是一个函数
then.call(x, resolvePromiseFn, rejectPromise);
// resolvePromiseFn 的入参是 y,执行 resolvePromise(promise2, y, resolve, reject);
// rejectPromise 的入参是 r ,reject promise with r

// 若 resolvePromise 和 rejectPromise 都调用了,第一个调用优先,后面的调用忽略

// 若调用then抛出异常e
	// 若 resolvePromise 或 rejectPromise 已经被调用,忽略
	// 则 reject promise with e as the reason

// 若then不是一个function,fulfill promise with x

Promise 实现

  1. class
class MPromise{
	constructor(){

	}
}
  1. 定义三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
  1. 设置初始状态
class MPromise {
    constructor() {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
    }
}
  1. resolve 和 reject 方法

    1. 将状态从pending变为 fulfilledrejected,且状态不可逆
    2. 入参分别是 valuereason
class MPromise {
    constructor() {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
    }

    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }

    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
}
  1. 根据规范,promise 接收一个参数

    1. 入参是一个函数,函数接收 resolvereject 两个参数
    2. 初始化时,promise立即执行这个函数,若有任何错误,立即抛出
class MPromise {
    constructor(fn) {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;

        try {
            fn(this.resolve.bind(this), this.reject.bind(this))
        } catch (e) {
            this.reject(e);
        }
    }

    // resolve方法
    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }

    // reject方法
    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
}
  1. then 方法

    1. then 接收两个参数,onFulfilledonRejected,两个参数均应是函数类型,若不是,应当被忽略
then(onFulfilled, onRejected){
        
}
  1. 检查参数是否为函数
isFunction(param) {
    return typeof param === 'function';
}

then(onFulfilled, onRejected) {
    // 若是function,返回onFulfilled,否则返回value
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value;

    // 若是function,返回onRejected,否则返回reason
    const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => reason;
}
  1. 根据规范,then方法返回的是一个promise
then(onFulfilled, onRejected) {
    // 若是function,返回onFulfilled,否则返回value
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value;

    // 若是function,返回onRejected,否则返回reason
    const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => reason;

    const promise2 = new MPromise((resolve, reject) => {});
    return promise2;
}
  1. 根据当前promise的状态,调用相应的函数
const promise2 = new MPromise((resolve, reject) => {
      switch (this.status) {
          case FULFILLED: {
              realOnFulfilled();
              break;
          }
          case REJECTED: {
              realOnRejected();
              break;
          }
      }
  });
  return promise2;
  1. 由于promise的状态有可能并非在一瞬间就变为 fulfilledrejected,可能仍为 pending 状态。因此需要监听promise的状态,当状态变为 fulfilledrejected 时,才能够执行 callback

    1. 获取所有 callback,新建两个数组,分别存放成功和失败的回调;调用 then 方法时,若仍是 pending 状态,将callback存入数组中。
class MPromise {
    FULFILLED_CALLBACK_LIST = [];
    REJECTED_CALLBACK_LIST = [];
}
then(onFulfilled, onRejected) {
    // 若是function,返回onFulfilled,否则返回value
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value;

    // 若是function,返回onRejected,否则返回reason
    const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => reason;

    const promise2 = new MPromise((resolve, reject) => {
        switch (this.status) {
            case FULFILLED: {
                realOnFulfilled();
                break;
            }
            case REJECTED: {
                realOnRejected();
                break;
            }
            case PENDING: {
                this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled);
                this.REJECTED_CALLBACK_LIST.push(realOnRejected);
                break;
            }
        }
    });
    return promise2;
}

b. 监听status发生变化时,执行相应的所有回调。

_status = PENDING;

get status() {
    return this._status;
}

set status(newStatus) {

    // 必须定义一个临时的`_status`,否则陷入循环
    this._status = newStatus;
    switch (newStatus) {
        case FULFILLED: {
            // 遍历执行回调
            this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                callback(this.value);
            })
            break;
        }
        case REJECTED: {
            this.REJECTED_CALLBACK_LIST.forEach(callback => {
                callback(this.reason);
            })
            break;
        }
    }
}
  1. then的返回值

    1. onFulfilledonRejected 抛出一个异常e,promise2 必须拒绝执行,并返回拒绝原因e。
then(onFulfilled, onRejected) {
    // 若是function,返回onFulfilled,否则返回value
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value;

    // 若是function,返回onRejected,否则返回reason
    const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => reason;

    const promise2 = new MPromise((resolve, reject) => {

        const fulfilledMicrotask = () => {
            try {
                realOnFulfilled(this.value);
            } catch (e) {
                reject(e);
            }
        }

        const rejectedMicrotask = () => {
            try {
                realOnRejected(this.reason);
            } catch (e) {
                reject(e);
            }
        }

        switch (this.status) {
            case FULFILLED: {
                realOnFulfilled();
                break;
            }
            case REJECTED: {
                realOnRejected();
                break;
            }
            case PENDING: {
                this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled);
                this.REJECTED_CALLBACK_LIST.push(realOnRejected);
                break;
            }
        }
    });
    return promise2;
}

b. 若onFulfilled 不是function,且promise1执行成功,promise2必须执行成功并返回相同的值。

c. 若onRejected 不是function,且promise2拒绝执行,promise2必须拒绝执行并返回相同的拒因。

// 若是function,返回onFulfilled,否则返回value
const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value;

// 若是function,返回onRejected,否则返回reason
const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => reason;

d. 若onFulfilled 或 onRejected 返回一个值x,运行 resolvePromise 方法

  1. resolvePromise
resolvePromise(promise2, x, resolve, reject) {
    // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
    // 这是为了防止死循环
    if (promise2 === x) {
        return reject(new TypeError('The promise and the return value are the same'));
    }

    if (x instanceof MPromise) {
        // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
        // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
        queueMicrotask(() => {
            x.then((y) => {
                this.resolvePromise(promise2, y, resolve, reject);
            }, reject);
        })
    } else if (typeof x === 'object' || this.isFunction(x)) {
        // 如果 x 为对象或者函数
        if (x === null) {
            // null也会被判断为对象
            return resolve(x);
        }

        let then = null;

        try {
            // 把 x.then 赋值给 then 
            // 这种首先保存 x.then 的引用,然后测试此引用,再调用此引用的处理,避免了对 x.then 属性的多次访问。这种预防措施在面对一些特殊访问器时十分重要,因为有的访问器每次访问都会导致其值的改变。
            then = x.then;
        } catch (error) {
            // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
            return reject(error);
        }

        // 如果 then 是函数
        if (this.isFunction(then)) {
            let called = false;
            // 将 x 作为函数的作用域 this 调用
            // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
            try {
                then.call(
                    x,
                    // 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
                    (y) => {
                        // 需要有一个变量called来保证只调用一次.
                        if (called) return;
                        called = true;
                        this.resolvePromise(promise2, y, resolve, reject);
                    },
                    // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                    (r) => {
                        if (called) return;
                        called = true;
                        reject(r);
                    });
            } catch (error) {
                // 如果调用 then 方法抛出了异常 e:
                if (called) return;

                // 否则以 e 为据因拒绝 promise
                reject(error);
            }
        } else {
            // 如果 then 不是函数,以 x 为参数执行 promise
            resolve(x);
        }
    } else {
        // 如果 x 不为对象或者函数,以 x 为参数执行 promise
        resolve(x);
    }
}
  1. onFulfilled 和 onRejected 是微任务,可以使用 queueMicrotask 包裹执行函数
const fulfilledMicrotask = () => {
    queueMicrotask(() => {
        try {
            // realOnFulfilled(this.value);
            const x = realOnFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
            reject(e);
        }
    })
}

const rejectedMicrotask = () => {
    queueMicrotask(() => {
        try {
            // realOnRejected(this.reason);
            const x = realOnRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
            reject(e);
        }
    })
}

switch (this.status) {
    case FULFILLED: {
        fulfilledMicrotask();
        break;
    }
    case REJECTED: {
        rejectedMicrotask();
        break;
    }
    case PENDING: {
        this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask);
        this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask);
        break;
    }
}
  1. 浅测试一下
const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        // console.log(1234)
        resolve(111);
    }, 1000);
}).then(console.log);

console.log(test);

setTimeout(() => {
    console.log(test);
}, 2000);
  1. catch 方法
catch(onRejected){
    return this.then(null, onRejected);
}
  1. 再浅测试一下
const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        // console.log(1234)
        // resolve(111);
        reject("reject一下")
    }, 1000);
}).catch(console.log);

console.log(test);

setTimeout(() => {
    console.log(test);
}, 2000);
  1. promise.resolve

将现有的对象转为Promise对象,若Promise.resolve 方法的参数,不是具有then方法的对象(thenable)对象,则返回一个新的Promise对象,且状态为 fulfilled

// promise.resolve
static resolve(value) {
    if (value instanceof MPromise) {
        return value;
    }

    return new MPromise((resolve) => {
        resolve(value);
    })
}
  1. promise.reject

返回一个新的Promise实例,实例的状态为rejected。

// promise.reject
static reject(reason) {
    return new MPromise((resolve, reject) => {
        reject(reason);
    })
}
  1. promise.race

const p = Promise.race([p1, p2, p3]);

多个Promise实例,封装成一个新的Promise实例

若p1, p2, p3中任意一个实例率先改变状态,p的状态也随之变化;率先改变状态的promise实例的返回值,传递给p的回调函数。

// promise.race
static race(promiseList) {
    return new MPromise((resolve, reject) => {
        const length = promiseList.length;

        if (length === 0) {
            return resolve();
        } else {
            for (let i = 0; i < length; i++) {
                MPromise.resolve(promiseList[i]).then((value) => {
                    return resolve(value);
                }, (reason) => {
                    return reject(reason);
                })
            }
        }
    })
}
  1. 浅测一下
const t1 = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(111);
    }, 1000);
})
const t2 = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(222);
    }, 500);
})
const t3 = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(333);
    }, 3000);
})

MPromise.race([t1, t2, t3]).then(console.log);

几个小问题

  1. 以下代码为何最后value值是undefined
const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(111);
    }, 1000);
}).then((value) => {
    console.log('then');
});

setTimeout(() => {
    console.log(test);
}, 3000)

答:这种写法相当于

then(value)=>{
	return undefined;
//return value;
}

显式return一个值,则不是undefined

  1. .then 返回的是一个新的Promise,那么原来的Promise实现的时候,有数组来存储回调函数,有何意义?

答:若使用链式回调,确实没有意义,因为每次回调函数的LIST都是空的数组;若是多次调用then,Promise实例是同一个,就有意义了。

const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(111);
    }, 1000);
})

test.then(() => {});
test.then(() => {});
test.then(() => {});
test.then(() => {});
  1. 以下代码中,为何在catch的回调中,打印promise,状态是pending
const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        reject(111);
    }, 1000);
}).catch((reason) => {
    console.log('报错' + reason);
    console.log(test)
});

setTimeout(() => {
    console.log(test);
}, 3000)

答:

  1. catch函数会返回一个新的Promise
  2. catch回调中,打印时,整个回调并未执行完成,因此此时状态是pending
  3. catch的回调函数,如果执行完成了,会改变这个新的Promise的状态为fulfilled

为什么会有微任务

典型的异步

最早的解决办法 callback

回调地狱

规范

为什么要用数组

  • then可以多次调用
  • 遇到 setTimeout 等,没有立即改变状态

链式调用

PromiseA+规范