promise解决什么问题:
- 成功的回调和失败的回调只会调用其中一个,而且只会调用一次
- 回调必须是异步调用的
- 回调之间支持链式调用
前置知识:
先看看new Promise接收什么参数,接受一个executor函数,这个executor会接受两个函数参数,这两个函数的调用会影响Promise内部的状态
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
再看看Promise.then(),接受两个函数参数,一个负责成功的回调一个负责错误的回调
Promise<unknown>.then<unknown, never>(onfulfilled?: ((value: unknown) => unknown) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<unknown>
那我们先从一般回调开始,回调是一种控制反转,函数如何调用什么时候调用将由内部函数决定
let result
function normalCb(cb) {
cb(result)
}
result = 'success'
normalCb(console.log)
但是我们这里只能监听到成功,错误的回调也需要处理
let result
let error
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
let state = PENDING
function normalErrorCb(cb, errorCb) {
switch(state) {
case FULFILLED: {
return cb(result)
}
case REJECTED: {
return errorCb(error)
}
default: {
console.log('pending...')
}
}
}
function setStatus(status, param) {
switch(status) {
case FULFILLED: {
state = FULFILLED
result = param
break
}
case REJECTED: {
state = REJECTED
error = param
break
}
default: {
console.log('pending...')
}
}
}
setStatus(FULFILLED, 'success')
normalErrorCb(console.log, console.error)
这样我们既能监听到成功和错误,但其实状态只应该改变一次,只会调用成功或错误,而且可以多次监听函数
// 状态只能改变一次,异步调用,并能接受多个回调
let result
let error
const PENDING = 0
const FULFILLED = 1
const REJECTED = 2
let state = PENDING
let done = false
const handlers = []
function normalErrorCb (cb, errorCb) {
setTimeout(() => {
switch (state) {
case FULFILLED: {
return cb(result)
}
case REJECTED: {
return errorCb(error)
}
case PENDING: {
return handlers.push({ cb, errorCb })
}
default: {
console.log('wrong status')
}
}
}, 0)
}
function setStatus (status, param) {
if (done) return
switch (status) {
case FULFILLED: {
done = true
state = FULFILLED
result = param
handlers.forEach((handler) => handler.cb(result))
break
}
case REJECTED: {
done = true
state = REJECTED
error = param
handlers.forEach((handler) => handler.errorCb(error))
break
}
default: {
console.log('pending...')
}
}
}
normalErrorCb(console.log, console.error)
setStatus(FULFILLED, 'success')
setStatus(REJECTED, 'error')
normalErrorCb(console.log.bind(undefined, 'one more'), console.error)
现在我们的函数状态只会改变一次,而且能多次监听,但是现在全局状态都是变量,我们要把他用函数包起来
// 我们声明了很多东西在全局作用域,我们应该把他包起来
function MockPromise (fn) {
let result
let error
const PENDING = 0
const FULFILLED = 1
const REJECTED = 2
let state = PENDING
let done = false
const handlers = []
this.then = function normalErrorCb (cb, errorCb) {
setTimeout(() => {
switch (state) {
case FULFILLED: {
return cb(result)
}
case REJECTED: {
return errorCb(error)
}
case PENDING: {
return handlers.push({ cb, errorCb })
}
default: {
console.log('wrong status')
}
}
}, 0)
}
function setStatus (status, param) {
if (done) return
switch (status) {
case FULFILLED: {
done = true
state = FULFILLED
result = param
handlers.forEach((handler) => handler.cb(result))
break
}
case REJECTED: {
done = true
state = REJECTED
error = param
handlers.forEach((handler) => handler.errorCb(error))
break
}
default: {
console.log('pending...')
}
}
}
fn(resolve, reject)
function resolve (successValue) {
setStatus(FULFILLED, successValue)
}
function reject (error) {
setStatus(REJECTED, error)
}
// 怎么暴露这个方法呢?一方面可以像then一样直接暴露,但是更好的方法是,通过构造函数的时候暴露出去,这样resolve,reject不会被外界改变
// this.resolve = function (successValue) {
// setStatus(FULFILLED, successValue)
// }
// this.reject = function (error) {
// setStatus(REJECTED, error)
// }
}
const p = new MockPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
reject('error')
}, 500)
})
p.then(console.log, console.error)
现在跟我们的promise已经很像了,但是无法做到链式调用,实现链式调用的时候还需要考虑,如果再then里面也调用了一个promise,那这个新返回的promise应该是加在这个promise的回调,只有这个promise成功执行后,新的promise才决议状态,所以resolve还有可能会得到一个promise
// 实现链式调用
function MockPromise (fn) {
let result
let error
const PENDING = 0
const FULFILLED = 1
const REJECTED = 2
let state = PENDING
let done = false
const handlers = []
this.then = function normalErrorCb (cb, errorCb) {
return new MockPromise((resolve, reject) => {
setTimeout(() => {
switch (state) {
case FULFILLED: {
const cbValue = cb(result) // 这里的cb返回值还是promise怎么办?这里的的resolve应该是等返回的promise结束后
return resolve(cbValue)
}
case REJECTED: {
const errorValue = errorCb(error)
reject(errorValue)
return errorValue
}
case PENDING: {
return handlers.push({ cb, errorCb })
}
default: {
console.log('wrong status')
}
}
}, 0)
})
}
function setStatus (status, param) {
if (done) return
switch (status) {
case FULFILLED: {
done = true
state = FULFILLED
result = param
handlers.forEach((handler) => handler.cb(result))
break
}
case REJECTED: {
done = true
state = REJECTED
error = param
handlers.forEach((handler) => handler.errorCb(error))
break
}
default: {
console.log('pending...')
}
}
}
fn(resolve, reject)
// function resolve (successValue) { // 默认是普通值直接传递给cb了
// setStatus(FULFILLED, successValue)
// }
function resolve (successValue) {
const t = typeof successValue
if (t === 'object' || t === 'function') {
const then = successValue.then
if (typeof then === 'function') {
then((result) => {
resolve(result)
}, (error) => {
reject(error)
})
}
} else {
setStatus(FULFILLED, successValue)
}
}
function reject (error) {
setStatus(REJECTED, error)
}
}
const p = new MockPromise((resolve, reject) => {
resolve('success')
reject('error')
})
p.then((result) => {
console.log(result)
return new MockPromise((resolve, reject) => {
resolve('otherSuccess')
})
}, console.error).then(console.log, console.error)
我们基本实现了这个promise,当然还有一些可以优化的地方,比如再抽象一些函数,可以看https://www.promisejs.org/implementing/
reference:
- https://www.promisejs.org/implementing/[Implementing promise]
- https://github.com/cyanxxx/blogcode/tree/master/how_to_realize_promise[本文所用的代码]