promise规范及应用

461 阅读9分钟

promise规范及应用

课程目标

  • promiseA+规范
  • 举例说明

知识要点

  • promiseA+术语
  • promiseA+规范

参考

promiseA+ API 链接

术语

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

规范

promise应该有三种状态:pendingfulfilledrejected

pending

  • 初始化状态,可改变
  • 一个promiseresolvereject前都处于这个状态
  • 可以能过resolve --> fulfilled状态
  • 可以能过reject --> rejected状态

fulfilled

  • 最终态,不可变
  • promiseresolve后会变成这个状态
  • 必须拥有一个value值,promiser成功后返回的值

rejected

  • 最终态,不可变
  • promisereject后变成这个状态
  • 必须拥有reasonpromiser失败后必须有一个错误的理由 ps:“不可变”意味着不变的身份,(即===),并不意味着深刻的不变性

promise的状态流转:

  • pending --> resolve(value) --> fulfilled
  • pedning --> reject(reason) --> rejected

then

promise必须提供一个then方法来访问当前或最终的值(value)或原因(reason)。 then接收两个参数:onFulfilledonRejected

promise.then(onFulfilled,onRejected)

onFulfilled & onRejected

  1. onFulfilledonReject两个参数如果不是函数,则会忽略。
  2. promise状态变成fulfilledrejected前,onFulfilledonReject不能调用且不能被多次调用
  3. onFulfilled的参数是valueonRejected的参数是reason
  4. onFulfilledonRejected应该是微任务 这里用queueMicrotask来实现微任务的调用

then可以被调用多次

  1. promise状态变成fulfilled后,所有的onFulfilled回调都需要按照then的顺序执行,也就是按照注册顺序执行(在实现的时候需要一个数组来存放多个onFulfilled的回调)
  2. promise状态变成rejected后,所有的onRejected回调都需要按照then的顺序执行,也就是按照注册顺序执行(在实现的时候需要一个数组来存放多个onFulfilled的回调)

then返回值

then应该返回一个Promise,是一个新的promise

promise2 = promise1.then(onFulfilled,onRejected)
  1. onFulfilledonRejected执行的结果为x,调用resolvePromise
  2. 如果onFulfilledonRejected执行时抛出异常,promise2需要被rejected。原因就是reason
  3. 如果onFulfiiled不是一个函数并且promise1fulfilledpromise2会以promise1value触发fulfilled
  4. 如果onRejected不是一个函数并且promise1rejectedpromise2会以promise1reason触发rejected

resolvePromise

resolvePromiseFn(promise2,x,resolve,reject)
  1. 如果promise2 === x,那么reject TypeError
  2. 如果x是一个promise
  • xpending状态,那么promise必须保持pending状态,直到x变成fulfilledrejected
  • xfulfilled,promise以相同的value返回
  • xrejectedpromise以相同的reason的理由拒绝
  1. 如果x是一个functionobject
  • let then = x.then
    
  • 如果x.then出错,那么reject promise with e as the reason
  • 如果then是一个函数,then.call(x,resolovePromise,rejectPromise),因为是函数的原因,当前的then指向不是当前的上下文,要把当前函数的this指向当前上下文。相当于then.call(x) => x.then
  1. 如果resolvePromise的入参是y,执行resolvePromise(promise2,y,resolve,reject)
  2. 如果resolvePromise的入参是rreject promise with r
  3. 如果resolvePromiseresolvePromise或者多次调用同一个参数,那么第一个调用优先,后面的调用忽略。
  4. 如果调用then抛出异常e,如果resolvePromiserejectPromise已被调用,就忽略。否则reject promise with e as the reason
  5. 如果then不是一个Function,fulfill promse with x

根据规范手写一个promise

  1. 定义一个promise类:
class DPromise {
    constructor() {
        
    }
}
  1. promise有三种状态,先定义一下:
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
  1. 初始化状态以及构造函数的的入参fn,构造函数传参fn要处理一下this指向,fn指向有可能不是当前实例。并且有任何错误的话都要reject。resolve和reject两个方法是要更改status的,从pending改成fulfilled | rejected,两个方法的入参分别是value和reason。
constructor(fn) {
    // 初始化状态
    this.status = PENDING;
    this.value = null;
    this.reason = null;
    // 同步任务,new promise时会执行
    try {
        fn(this.resolve.bind(this),this.reject.bind(this));
    } catch (e){
        this.reject(e);
    }

}
resolve(value){
    if (this.status === PENDING){
        this.status = FULFILLED;
        this.value= value;
    }
}
rejet(reason){
    if (this.status === PENDING){
        this.status = REJECTED;
        this.reason = reason;
    }
}
  1. then方法会有两个参数onFulfilled,onRejected,两个参数必须是函数,首先要判断一下是不是函数,如果不是要处理一下,这里是手写一个方法isFunction判断。
then(onFulfilled, onRejected) {
        // 如果是一个函数就正常返回,不是一个函数给一个默认值
        const realOnFulfilled = this.isFunction(onFulfilled) ?  onFulfilled : (value) => {return value;}
        const realOnReject = this.isFunction(onrejected) ? onRejected : (reason) => {throw reason}
        // then返回一个promise,根据不同的promise状态调用不同的函数
        const promise2 = new Promise((resolve,reject) => {
            switch(this.status) {
                case 'FULFILLED': {
                    realOnFulfilled();
                    break;
                }
                case 'REJECTED': {
                    realOnReject();
                    break;
                }
            }
        });
        return promise2;
    }
isFunction(param) {
    return typeof param === 'function'
}

基本上就算写完,可以测试一下

new Promise((resolve,reject) => {
    console.info(1);
    resove(1)
}).then(value => {
    
})

在构造函数里执行resolve(1)之后呢,status的状态变成fulfilled,是没问题的。因为是同步执行。如果改成异步呢?

new DPromise((resolve,reject) => {
    console.info(1);
    setTimeout(() =>{
        resolve();
    },1000)
}).then(value => {

})

在1s之后再运行,status状态还没变成fulfilled或rejected,有可能还在pending状态,所以需要一个状态的监听机制,当状态变成fulfilled或者rejected后,再执行callballck。

// 数组接收promise值
FULFILLED_CALLBACK_TASK=[]
REJECTED_CALLBACK_TASK=[]
_status = PENDING

// ES6 有提供getter和setter,当status改变时,去做什么事情
// 为什么非要需要一个私有变量?因为会陷入死循环
// 解决异步问题需要一个状态的监听机制,当状态变成fulfilled或者rejected后,再执行callballck。
// 为什么需要get和set  尽量每个方法做到单一处理,一个方法只做一件事。
get status() {
    return this._status;
}
set status(newStatus) {
    this._status = newStatus;
    switch (newStatus) {
        case FULFILLED: {
            this.FULFILLED_CALLBACK_TASK.forEach(callback => {
                callback(this.value)
            })
            break;
        }
        case REJECTED: {
            this.REJECTED_CALLBACK_TASK.forEach(callback => {
                callback(this.reason)
            })
            break;
        }
    }
}

resolvePromise代码

resolvePromise(promise2,x,resolve,reject){
        // 如果promise2 === x就报错
        if(promise2 === x){
            return reject(new TypeError('the promise an the return value are the same'))
        }
        if(x instanceof DPromise){
            // 如果 x 为promise,继续执行 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也会被判断为对象,直接resolve
                return resolve(x); 
            }

            let then = null;
            try {
                // 把x.then 赋值给then
                then =  x.then;
            }catch(error){
                // 如果获取x.then的值抛出错误error,则以error为原因拒绝promise
                return reject(error)
            }

            // 如果then是一个函数
            if(this.isFunction(then)){
                // then不需要调用多次
                // 需要一个变量called来保证只调用一次
                let called = false;
                try {
                    // 将 x 作为函数的作用域 this 调用 
                    then.call(
                        x,
                        // 如果resolvePromise以值 y 为函数被调用,则运行resolvePromise
                        (y) => {
                            if(called){
                                return;
                            }
                            called =  true;
                            this.resolvePromise(promise2, y, resolve, reject);
                        },
                        (r) => {
                            if(called){
                                return;
                            }
                            called =  true;
                            reject(r);
                        }
                    )
                }catch(error){
                    reject(error)
                }
            }else{
                // 如果x不是函数或对象,以x为参数执行promise
                resolve(x)
            }
        } else {
            resolve(x);
        }
    }

完整代码

代码内容还需多复习,规范也要熟知,有一些规范直接放在代码注释上了。

// 手写promise
// 2.定义三种状态类型
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// 1.初始化class
class DPromise {
    // 数组接收promise值
    FULFILLED_CALLBACK_TASK=[]
    REJECTED_CALLBACK_TASK=[]
    _status = PENDING
    // 5.Promise构造函数的入参,new promise的时候,就要执行构造函数,并且有任何错误都要被reject出去,比如:new Promise((resove,reject) => {})
    // 
    /**
     * 常见面试题  问哪些是同步任务哪些是异步任务
     * console.info('begin');
     * new Promise((resove,reject) =>{
     * console.info(1);
     * }).then(() =>{
     *  console.info(2);
     * })
     * 
     * begin和1是同步任务,promise的构造函数是同步任务
     * 2是异步任务
     * 
     * 
    */
    constructor(fn) {
        // 3.初始化状态
        this.status = PENDING;
        this.value = null;
        this.reason = null;
        // 同步任务,new promise时会执行
        try {
            fn(this.resolve.bind(this),this.reject.bind(this));
        } catch (e){
            this.reject(e);
        }
        
    }
    // ES6 有提供getter和setter,当status改变时,去做什么事情
    // 为什么非要需要一个私有变量?因为会陷入死循环
    // 解决异步问题需要一个状态的监听机制,当状态变成fulfilled或者rejected后,再执行callballck。
    //为什么需要get和set  尽量每个方法做到单一处理,一个方法只做一件事。
    get status() {
        return this._status;
    }
    set status(newStatus) {
        this._status = newStatus;
        switch (newStatus) {
            case FULFILLED: {
                this.FULFILLED_CALLBACK_TASK.forEach(callback => {
                    callback(this.value)
                })
                break;
            }
            case REJECTED: {
                this.REJECTED_CALLBACK_TASK.forEach(callback => {
                    callback(this.reason)
                })
                break;
            }
        }
    }
    // 4.resolve / reject
    resolve(value){
        if (this.status === PENDING){
            this.status = FULFILLED;
            this.value= value;
        }
    }
    rejet(reason){
        if (this.status === PENDING){
            this.status = REJECTED;
            this.reason = reason;
        }
    }
    // 6. then
    then(onFulfilled, onRejected) {
        // onFulfilled和onRejected是微任务,可以用queueMicrotask包裹执行函数
        queueMicrotask(() => {
            const fulfilledMicrotask= () => {
                try {
                    // 如果onFulfilled或者onReject返回一个值x,则运行resolvePromise方法
                   const x = realOnFulfilled(this.value);
                   this.resolvePromise(promise2, x, resolve, reject);
                }catch(e) {
                    // 如果onFulfilled或onReject抛出一个异常e ,则promise2必须拒绝执行,并返回拒绝原因e,手动catch
                    reject(e)
                }
            }
        })
        queueMicrotask(() => {
            const rejectedMicrotask= () => {
                try {
                     // 如果onFulfilled或者onReject返回一个值x,则运行resolvePromise方法
                   const x = realOnFulfilled(this.value)
                   this.resolvePromise(promise2, x, resolve, reject)
                }catch(e) {
                    // 如果onFulfilled或onReject抛出一个异常e ,则promise2必须拒绝执行,并返回拒绝原因e,手动catch
                    reject(e)
                }
            }
        });
        // 如果onRejected不是函数且proimse1被拒绝执行,promise2必须拒绝执行并返回相同的原因。
        const realOnFulfilled = this.isFunction(onFulfilled) ?  onFulfilled : (value) => {return value;}
        const realOnReject = this.isFunction(onrejected) ? onRejected : (reason) => {throw reason}
        // then返回一个promise
        const promise2 = new Promise((resolve,reject) => {
            switch(this.status) {
                case 'PENDING':{
                    this.FULFILLED_CALLBACK_REST.push(fulfilledMicrotask);
                    this.REJECTED_CALLBACK_REST.push(rejectedMicrotask);
                }
                case 'FULFILLED': {
                    fulfilledMicrotask();
                    break;
                }
                case 'REJECTED': {
                    rejectedMicrotask();
                    break;
                }
            }
        });
        return promise2;
    }
    resolvePromise(promise2,x,resolve,reject){
        // 如果promise2 === x 就报错
        if(promise2 === x){
            return reject(new TypeError('the promise an the return value are the same'))
        }
        if(x instanceof DPromise){
            // 如果 x 为promise,继续执行 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也会被判断为对象,直接resolve
                return resolve(x); 
            }

            let then = null;
            try {
                // 把x.then 赋值给then
                then =  x.then;
            }catch(error){
                // 如果获取x.then的值抛出错误error,则以error为原因拒绝promise
                return reject(error)
            }

            // 如果then是一个函数
            if(this.isFunction(then)){
                // then不需要调用多次
                // 需要一个变量called来保证只调用一次
                let called = false;
                try {
                    // 将 x 作为函数的作用域 this 调用 
                    then.call(
                        x,
                        // 如果resolvePromise以值 y 为函数被调用,则运行resolvePromise
                        (y) => {
                            if(called){
                                return;
                            }
                            called =  true;
                            this.resolvePromise(promise2, y, resolve, reject);
                        },
                        (r) => {
                            if(called){
                                return;
                            }
                            called =  true;
                            reject(r);
                        }
                    )
                }catch(error){
                    reject(error)
                }
            }else{
                // 如果x不是函数或对象,以x为参数执行promise
                resolve(x)
            }
        } else {
            resolve(x);
        }
    }
    isFunction(param) {
        return typeof param === 'function'
    }
}

手写一个promise.all

promise.all是直接挂载在promise方法上。切记切记

//all方法传参是一个数组
Promise.all = (arr) => {
    return new Promise((resolve,reject) => {
        //记录参数数组执行结果
        let res = [];
        // 记录参数数组是否结束
        let count = 0;
        for (let i = 0; i < arr.length; i++) {
            // 防止数组里并不是promise对象,有可能是function或object等其它值,强制转换为promise
            Promise.resolve(arr[i])
                .then((value) => {  
                    // 存放执行结果,不能用push,all方法 
                    res[i] = value;
                    // 返回结果跟参数一一对应
                    count++;
                    /**
                     * 
                     * county计算器判断参数数组是否执行结束,不能用res的长度跟arr的长度做对比,
                     * 因为for是同步,promise是异步,如果第一个promise还没执行完,
                     * 第二个promise比第一个promise执行速度快(后面的promiser执行速度比前面快),
                     * res[i]的长度就有问题,会认为promise已经结束,但其实并没有。
                     * if判断只能放在这里,for是同步,promise是异步,如果放到promise方法外,结果就是undefined
                     * 
                     */ 
                    if(count === arr.length){
                        resolve(res);
                    }
                }).catch((reson) =>{
                    reject(reson);
                })
        }
    })
}

手写promise.finally

是挂载在原型对象上的一个方法。切记切记

Promise.prototype.finally = function (callback) {
    // 返回结果对象来者不拒
    return this.then((value) => {
        return Promise.resolve(callback()).then(() => {value})
    },
    (err) => {
        return Promise.reject(callback()).then(() => {throw err})
    })
}

ps:记录一下笔记,方便自己查阅,有问题或不对的地方请各位大佬积极指出,谢谢。