promise 详解

234 阅读6分钟

**# Promise/Promise/A+规范 ** promise 是一个有着then方法的对象或者函数,行为遵循本规范

thenable 是一个有then方法的对象或者函数 value value是promise状态成功时的值,也就是俗称的resolve的参数,类型可以是undefined,thenable 函数,或者boolean等 reason 是一个promise状态失败的值,reject的参数,表示拒绝的原因 exception 是一个使用throw抛出的异常值

规范

一. promise的状态 三种

1.promise 应该有三种状态,注意的是之间的流转关系
  1. pending 初始的状态 可改变 一个promise在最开始的时候,在resove和reject之前,都处于这种状态,而这种状态就叫做pending 它可以通过resolve这样的一个函数 可以将pending状态转换为fulfilled pending-> resolve() -> fulfilled 同理 在进行reject这样的一个函数,可以将pending状态转为rejected pedning-> reject() -> rejeted 总结:promise的状态完全是由resolve和reject两个函数来控制的

2.fulfilled 俗称也就是状态成功的值 结论:是一个最终态 不可更改 如果一个pending通过resolve变成了fulfilled,意味着这个值不可能再被改变 由来:一个promise 被resolve 之后会变成这个值 参数: 必须拥有一个value值 常见的resolve() 其实也有一个值,只是这个值叫做undefined

  1. rejected 结论:是一个最终态 不可更改 如果一个pending通过reject变成了rejected,意味着这个值不可能再被改变 参数: 必须拥有一个reason值

因此,一个pending的改变 只有两种流程

pending -》 resolve(value) -》 fulfilled

pending -》 reject(reason)-》 rejected

then这个概念 这个概念很重要

一般通过.then()来获取成功过之后的结果 因此 一个promise 是应该有一个.then的方法的 promise 是否只能访问value? 不是 比如:new Promise().then().catch() 因此不止有value

语法: promise.then(onfulfilled, onPrejected) 一.参数要求

  1. onFulfilled 必须是函数类型,如果不是函数类型,应该被 忽略
  2. onPrejected 必须是函数类型,如果不是函数类型,应该被 忽略 所谓一个忽略,其实是指当你传给我的不是一个函数,我会把接收到的值再传给你

二. onFulfilled 特性 1.首先它是一个函数 在promise 变成fulfilled时,应该调用onFulFilled, 参数时value 在promise 变成fulfilled 之前,不应该被调用 只能被调用一次(可能需要一个变量来限制执行次数)

三. onPrejected 特性 首先它是一个函数 在promise 变成rejected时,应该调用onPrejected, 参数时reason 在promise 变成rejected 之前,不应该被调用 只能被调用一次(可能需要一个变量来限制执行次数)

四. onFulfilled和onPrejected 在执行时应该是微任务 因此用 queryMicrotask 来实现微任务的调用

五 .then 这个方法可以被调用多次

1.promise 规定变成fulfilled之后,所有的onFulfilled 回调哦都需要按照then的顺序执行(用数组存放 onFulfilled list)

2.promise 规定变成rejected之后,所有的onPrejected 回调哦都需要按照then的顺序执行(用数组存放 onPrejected list)

六. .then 的返回值 then应该返回一个promise

 语法如下:
 **promise2 = promise1.then(xx,xx)**
 

onfulfilled 或者 onRejected 执行结果为x,调用 resolvePromise 这个方法 如果 onfulfilled 或者 onRejected 执行中抛出异常e, promise2就要被reject 如果 onfulfilled 不是一个函数,那么promise2会以promise1 的value触发fulfilled 如果 onRejected 不是一个函数,那么promise2会以promise1 的 reason 触发rejected

七. resolvePromise 语法:

resolvePsomise(promise2,x,resolve,reject)

**romise2 是 新的peomise 因为.then要返回一个新的promise
x 是 onfulfilled 或者 onRejected 执行结果为x
resolve,reject 是改变状态的**

1.如果 promise2 和 x 相等,那么reject typeError, 此时会返回一个死循环

2.如果x真的是一个promise, 我们要用从状态剖析, 如果x是一个pending 那么promise一定是继续pending,直到变成了其他状态

3.如果 x 是一个fulfilled, resolve same value 及传什么,返回什么

4.如果 x 是一个 rejected, resolve same reason 及传什么,返回什么

5.如果x是一个object 或者fn 如果取then报错

    try
      let then = x.then
    catch(e)
     reject promise e

如果 x.then 是一个函数,那么尽可能让x来调用,即 then.call(x,resolve PromiseFn,rejectPromiseFn) 如果 x.then 不是一个函数, 直接返回 x fulfilled promise x

规范结束

一步步实现一个promise class版本

1.平时用promise的时候,一般是new Promise() 因此需要一个构造函数

    class Mpromise {
    // 成功的回调
    FULFILLED_CALLBACK_LIST = []
    // 失败状态的回调
    REJECTED_CALLBACK_LIST = []
    // 私有变量, 存储真正的status
    _status = PENDING
        constructor() {}
    }
  1. 定义三种状态类型 pending,fulfilled,rejected
    const PEDNGING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'

3.设置一个初始状态

    constructor() {
        // 初始状态 初始状态为pending
        this.status = PEDNGING
        // 初始状态 fulfilled 为pending
        this.value = null
        // 初始状态 rejected 为pending
        this.reason = null
    }
  1. 设置两个默认返回的函数 resolve 和 reject

    1. 这两个方法用来更改status, 从pending变为 fulfilled 或者rejected
    2. 状态改变时 需要判断 当前状态是不是pending
    3. 函数入参是value和reason
    resolve(value) {
        if(this.status === PEDNGING){
            this.value = value
            this.status = FULFILLED
        }
    }
    reject(reason) {
        if(this.status === PEDNGING){
            this.reason = reason
            this.status = REJECTED
        }
    }
  1. promise 构造时的入参

    1.入参是一个函数,函数接受 resolve和reject两个参数

    1. 注意在初始化promise的时候,就要执行这个函数,并且有任何报错,都要reject出去

    constructor(fn) {
        // 初始状态 初始状态为pending
        this.status = PEDNGING
        // 初始状态 fulfilled 为pending
        this.value = null
        // 初始状态 rejected 为pending
        this.reason = null
        关注这里
        try {
            fn(this.resolve.bind(this), this.reject.bind(this))
        }
        catch(e){
            this.reject(e)
        }
    }
  1. then 方法

    1. then接受两个参数,一个是onFulfilled,一个是onRejected
        then(onFulfilled, onRejected){}
    
    1. 检查参数 做默认值处理
         then(onFulfilled, onRejected) {
                 const realOnfulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => value
                 const realOnrejected = this.isFunction(onRejected) ? onRejected : (reason) => reason
         }
    
    1. 处理返回值 .then的返回值是一个整体的promise,且是一个新的promise,所以.then的实现里先用promise包一下

then(onFulfilled, onRejected) {
        const realOnfulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => value
        const realOnrejected = this.isFunction(onRejected) ? onRejected : (reason) => reason
        const promise2 = new Mpromise((resolve,reject) => {})
        return promise2 
    }
  1. 根据当前promise的状态,调用不同的函数
then(onFulfilled, onRejected) {
        const realOnfulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => value
        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
                }
            }
        })
        return promise2 
    }
  1. 如果promise的状态不是同步改变 该如何判断状态,且执行对应操作
    1. 所以先拿到callback,在某个时机去执行他,所以新建两个数组来存储成功和失败的回调
then(onFulfilled, onRejected) {
        const realOnfulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => value
        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 PEDNGING:{
                    this.FULFILLED_CALLBACK_LIST.push(realOnfulfilled)
                    this.REJECTED_CALLBACK_LIST.push(realOnrejected)
                }
            }
        })
        return promise2 
    }

2.什么时候去执行

这里用的是es6的getter和setter 做相应处理

    get status() {
        return this._status
    }

    set status(newStatus) {
        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)
                })
            }
        }
    }
  1. then的返回值 如果onFulfilled 或者 onRejected 报错 要try出去

    const fulfilledMicrotask = () => {
        try{
            const x = realOnfulfilled(this.value)
            this.resolvePromise(promise2,x,resolve,reject)
        } catch(e) {
            reject(e)
        }
    }
    const rejectedMicrotask = () => {
        try{
            const x = realOnrejected(this.reason)
            this.resolvePromise(promise2,x,resolve,reject)
        } catch(e) {
            reject(e)
        }
    }
  1. resolvePromise 是个啥

resolvePromise(promise2,x,resolve,reject) {
        // 如果promise2 和 x 相等 抛出一个error
        if(promise2 === x){
            return reject(new TypeError('The promise and the return avlue are the same'))
        }
        if(x instanceof Mpromise){
            queryMicrotask(() => {
                x.then(y => {
                    this.resolvePromise(promise2, x,resolve,reject)
                }, reject)
            })
            

        } else if( typeof x === 'object' || this.isFunction(x) ) {

            if(x === null){
                return resolve(x)
            }
            let then = null
            try{
                then = x.then
            } catch(e){
                // 如果取x.then值报错了,那么以e为reason reject promise
                return reject(e)
            }
            // 如果then是函数
            if(this.isFunction(then)){

                let called = false
                try{

                    then.call(
                        x,
                        (y) => {
                            if(called ){
                                return
                            }
                            called = true
                            this.resolvePromise(promise2,y,resolve,reject)
                        },
                        (r) => {
                            if(called ){
                                return
                            }
                            called = true
                            reject(r)
                        }
                    )
                } catch(error){
                    if(called){
                        return
                    }
                    reject(error)
                }

            } else {
                resolve(x)
            }

        } else {
            resolve(x)
        }
    }
  1. 微任务
const promise2 = new Mpromise((resolve,reject) => {
            const fulfilledMicrotask = () => {
                queueMicrotask(() => {
                    try{
                        const x = realOnfulfilled(this.value)
                        this.resolvePromise(promise2,x,resolve,reject)
                    } catch(e) {
                        reject(e)
                    }
                })
                
            }
            const rejectedMicrotask = () => {
                queueMicrotask(() => {
                    try{
                        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)
                }
            }
        })