侧边栏壁纸
博主头像
woku博主等级

成功的路上并不拥挤

  • 累计撰写 50 篇文章
  • 累计创建 13 个标签
  • 累计收到 3 条评论

Promise A+

woku
2022-06-14 / 0 评论 / 1 点赞 / 145 阅读 / 11,881 字

Promise A+规范:

详细地址可参考:https://promisesaplus.com/

Terminology(术语)

  1. “promise” is an object or function with a then method whose behavior conforms to this specification.
  2. “thenable” is an object or function that defines a then method.
  3. “value” is any legal JavaScript value (including undefined, a thenable, or a promise).
  4. “exception” is a value that is thrown using the throw statement.
  5. “reason” is a value that indicates why a promise was rejected.

image.png

Promise States

  • pending
  • fulfilled
  • rejected

当状态是pending时候,可能会转变成fulfilled或rejected状态

  1. When fulfilled, a promise:
    1. must not transition to any other state. 不能转变到其他状态
    2. must have a value, which must not change. 必须有一个value,并且这个value的引用地址不能变化
  2. When rejected, a promise:
    1. must not transition to any other state.
    2. must have a reason, which must not change.

Here, “must not change” means immutable identity (i.e. ===), but does not imply deep immutability.
value = { a: 1, b: 2 } -> { a: 1, b: 2, c: 3} 这种是可以的, 地址没有变

Promise实现基础版

const PENDING = 'PENDING',
      FULFILLED = 'FULFILLED',
      REJECTED = 'REJECTED'
class MyPromise {
    constructor(executor) {
        // 状态
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
            }
        }
        const reject = reason => {
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
            }
        }
        try {
            executor(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
    then(onFulfilled, onRejected) {
        if (this.status === FULFILLED) {
            onFulfilled(this.value)
        }
        if (this.status === REJECTED) {
            onRejected(this.reason)
        }
      
    }
}
module.exports = MyPromise

处理promise中异步与多次调用

如果executor里面有异步的程序,当执行到then的时候,promise实例的状态还是pending, then里面并未处理状态为pending的情况。
使用发布订阅的思想处理 -> 在还没到的时候,先存起来(订阅)。到了时间点再拿出来(发布)

const PENDING = 'PENDING',
      FULFILLED = 'FULFILLED',
      REJECTED = 'REJECTED'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                if (this.onFulfilledCallbacks.length) {
                    // 发布
                    this.onFulfilledCallbacks.forEach(fn => fn())
                }
            }
        }
        const reject = reason => {
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                if (this.onRejectedCallbacks.length) {
                    // 发布
                    this.onRejectedCallbacks.forEach(fn => fn())
                }
            }
        }
        try {
            executor(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
    then(onFulfilled, onRejected) {
        if (this.status === FULFILLED) {
            onFulfilled(this.value)
        }
        if (this.status === REJECTED) {
            onRejected(this.reason)
        }
        if (this.status === PENDING) {
            // 订阅回调
            this.onFulfilledCallbacks.push(() => {
                onFulfilled(this.value)
            })
            this.onRejectedCallbacks.push(() => {
                onRejected(this.reason)
            })
        }
    }
}
module.exports = MyPromise
  • 在new的时候,初始化onFulfilledCallbacksonRejectedCallbacks数据
  • 执行then方法,如果状态是pending,那么把成功的回调,和失败的回调先存起来,到了时间点再调用
  • promise.then可以有多个,那么就有多个成功或失败的回调,onFulfilledCallbacksonRejectedCallbacks里存储的是所有的回调
  • resolve执行的时候,依次执行onFulfilledCallbacks回调
  • reject执行的时候,依次执行onRejectedCallbacks的回调

原生Promise中链式调用的特征

Promise中链式调用

其实就是在then中又返回了一个新的Promise实例,这样才能形成一层层的链式调用

promise.then(() => {
    // return new Promise(...)
}).then(() => {
    
})

注意下面两种写法的区别:


const promise = new Promise((resolve, reject) => {
    resolve('success')
})
let promise2 = promise.then(() => {

}).then(() => {

})
// promise2是经过第一层then返回的新的Promise后,再then返回的新Promise
const promise = new Promise((resolve, reject) => {
    resolve('success')
})

let promise2 = promise.then(() => {

})
promise2.then(() => {

})
// promise2是经过第一层then返回的新的Promise

特征

  • 通过return来传递结果
const promise = new Promise((resolve, reject) => {
    resolve('success')
})
promise.then(res => {
    return res
}, err => {
}).then(res => {
    console.log('resolve', res)
})
// 通过return的方式将结果传给下一个then
// 走成功回调
  • 通过新的Promise, resolve结果(不管有没有异步,走成功回调)
const promise = new Promise((resolve, reject) => {
    resolve('success')
})
promise.then(res => {
    return new Promise((resolve, reject) => {
        resolve(res)
    })
}, err => {
}).then(res => {
    console.log('resolve', res)
})
  • 通过新的Promise,reject原因(不管有没有异步,走失败回调)
const promise = new Promise((resolve, reject) => {
    resolve('success')
})
promise.then(res => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('error')
        }, 1000);
    })
}, err => {
}).then(value => {
    console.log('resolve', value)
}, reason => {
    console.log('rejected', reason)
})
// 走第二个失败的回调
// 一秒后打印 rejected error
  • then走了失败的回调后,再走then
const promise = new Promise((resolve, reject) => {
    resolve('success')
})
promise.then(res => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('error')
        }, 1000);
    })
}, err => {
}).then(value => {
    console.log('resolve', value)
}, reason => {
    console.log('rejected', reason)
}).then(value => {
    console.log('resolve2: ', value)
}, reason => {
    console.log('rejected2: ', reason)
})
// rejected error
// resolve2:  undefined

第一次走了失败的回调,默认会返回undefined
接着调用then会走到成功的回调,value就是undefined

  • then中throw Error
const promise = new Promise((resolve, reject) => {
    resolve('success')
})
promise.then(res => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('error')
        }, 1000);
    })
}, err => {
}).then(value => {
    console.log('resolve', value)
}, reason => {
    console.log('rejected', reason)
}).then(value => {
    throw new Error('error')
}).then(value => {
    console.log(value)
}, reason => {
    console.log('exeption', reason)
})
// 抛出异常会走到下一个then的失败回调

  • catch捕获异常
const promise = new Promise((resolve, reject) => {
    resolve('success')
})
promise.then(res => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('error')
        }, 1000);
    })
}, err => {
}).then(value => {
    console.log('resolve', value)
}, reason => {
    console.log('rejected', reason)
}).then(value => {
    throw new Error('error')
}).then(value => {
    console.log(value)
}).catch(err => {
    console.log('catch', err)
})
// 如果没有失败的回调,就走catch捕获
// 如果有失败的回调,就不走catch捕获
  • catch后走then
const promise = new Promise((resolve, reject) => {
    resolve('success')
})
promise.then(res => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('error')
        }, 1000);
    })
}, err => {
}).then(value => {
    console.log('resolve', value)
}, reason => {
    console.log('rejected', reason)
}).then(value => {
    throw new Error('error')
}).then(value => {
    console.log(value)
}).catch(err => {
    console.log('catch', err)
    return 'success'
}).then(value => {
    console.log('catch', value)
})

catch在Promise源码中其实就是一个then,遵循着then的运行规则

成功的条件:

  1. then 里面 ,return javascript 一个普通值,包括undefined
  2. then里面,return 新的Promise, 成功态的结果

失败的条件:
1.then里面,return 新的Promise, 失败态的原因
2.then里面,抛出了异常

myPromise实现链式调用

then里面返回新的myPromise实例

使用x来接收onFulfilledonRejected函数的返回值
如果在onFulfilledonRejected里抛出了异常,进行try...catch捕获,直接reject

const PENDING = 'PENDING',
    FULFILLED = 'FULFILLED',
    REJECTED = 'REJECTED'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                if (this.onFulfilledCallbacks.length) {
                    // 发布
                    this.onFulfilledCallbacks.forEach(fn => fn())
                }
            }
        }
        const reject = reason => {
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                if (this.onRejectedCallbacks.length) {
                    // 发布
                    this.onRejectedCallbacks.forEach(fn => fn())
                }
            }
        }
        try {
            executor(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
    then(onFulfilled, onRejected) {
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                    } catch (err) {
                        reject(err)
                    }
                }, 0);
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                    } catch (err) {
                        reject(err)
                    }
                }, 0)

            }
            if (this.status === PENDING) {
                // 订阅回调
                this.onFulfilledCallbacks.push(() => {
                    try {
                        let x = onFulfilled(this.value)
                    } catch (err) {
                        reject(err)
                    }

                })
                this.onRejectedCallbacks.push(() => {
                    try {
                        let x = onRejected(this.reason)
                    } catch (err) {
                        reject(err)
                    }
                })
            }
        })
        return promise2

    }
}
module.exports = MyPromise

处理x

x有可能是一个普通的Javascript的值,也有可能是一个myPromise实例
promiseResolve函数对x进行处理
If promise and x refer to the same object, reject promise with a TypeError as the reason.

function promiseResolve(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('same Object'))
    }
}

Otherwise, if x is an object or function
Let then be x.then - 获取x的then方法
If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason. - 在获取then方法时,会被defineProperty进行属性劫持,在getter中如果抛出异常,直接reject。

function promiseResolve(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('same Object'))
    }
    if ((typeof x === 'object' && x != null) || typeof x === 'function') {

    } else {
        resolve(x)
    }
}


If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where:

  1. If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
  2. If/when rejectPromise is called with a reason r, reject promise with r.
  3. If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.

如果then是一个函数,那么返回的就是一个Promise,
调用then方法传入resolvePromise和rejectPromise
resolvePromise回调执行时候,resolve成功,值为y
rejectPromise回调执行时候,reject失败,原因为r
如果y还是一个Promise,需要递归的调用promiseResolve

function promiseResolve(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('same Object'))
    }
    if ((typeof x === 'object' && x != null) || typeof x === 'function') {
        try {
            let then = x.then,
                called = false
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) return
                    called = true
                    promiseResolve(promise2, y, resolve, reject)
                }, r => {
                    if (called) return
                    called = true
                    reject(r)
                })
            } else {
                if (called) return
                called = true
                resolve(x)
            }
        } catch (err) {
            reject(err)
        }
    } else {
        resolve(x)
    }
}

处理then穿透

可以通过then().then().then()这种方式
then里面没有resolvePromiserejectPromise,可以给他默认值

 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
 onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw new Error(reason) }
const MyPromise = require('./myPromise')
const promise = new MyPromise((resolve, reject) => {
    resolve('success')
})

const promise2 = promise.then(value => {
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve(value + '123')
        }, 1000);
    })
}).then().then().then(value => {
    console.log(value)
}, reason => {
    console.log(reason, '222')
})

catch语法糖

    catch(errorCallback) {
        this.then(null, errorCallback)
    }

本节完整MyPromise代码

const PENDING = 'PENDING',
    FULFILLED = 'FULFILLED',
    REJECTED = 'REJECTED'

function promiseResolve(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('same Object'))
    }
    if ((typeof x === 'object' && x != null) || typeof x === 'function') {
        try {
            let then = x.then,
                caller = false
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (caller) return
                    caller = true
                    promiseResolve(promise2, y, resolve, reject)
                }, r => {
                    if (caller) return
                    caller = true
                    reject(r)
                })
            } else {
                if (caller) return
                caller = true
                resolve(x)
            }
        } catch (err) {
            reject(err)
        }
    } else {
        resolve(x)
    }
}
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) => {
            if (value instanceof MyPromise) {
                value.then(resolve, reject)
                return
            }
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                if (this.onFulfilledCallbacks.length) {
                    // 发布
                    this.onFulfilledCallbacks.forEach(fn => fn())
                }
            }
        }
        const reject = reason => {
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                if (this.onRejectedCallbacks.length) {
                    // 发布
                    this.onRejectedCallbacks.forEach(fn => fn())
                }
            }
        }
        try {
            executor(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        onRejected = typeof onRejected === 'function' ? onRejected : reason => {
            throw new Error(reason)
        }
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                        promiseResolve(promise2, x, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                }, 0);
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        promiseResolve(promise2, x, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                }, 0)

            }
            if (this.status === PENDING) {
                // 订阅回调
                this.onFulfilledCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value)
                            promiseResolve(promise2, x, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    }, 0);


                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason)
                            promiseResolve(promise2, x, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    }, 0);
                })
            }
        })
        return promise2

    }
    catch (errorCallback) {
        this.then(null, errorCallback)
    }
    static resolve(value) {
        if (value instanceof MyPromise) {
            value.then(MyPromise.resolve, MyPromise.reject)
        }
        return new MyPromise((resolve, reject) => {
            resolve(value)
        })
    }
    static reject(reason) {
        return new MyPromise((resolve, reject) => {
            reject(reason)
        })
    }
}
module.exports = MyPromise
1

评论区