Skip to main content

手写Promise

tip

第一步

Pormise 是一个类,且有 pending、fulfilled、rejected 三种状态,初步得出代码如下

type StateType = 'pending' | 'fulfilled' | 'rejected'

class Promise1 {
protected state!: StateType
protected value!: any
protected reason!: any

constructor(executor: Function) {
this.state = 'pending'
this.value = undefined
this.reason = undefined

const resolve = (val: any) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = val
}
}

const rejecte = (reason: any) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
}
}

// 如果executor执行报错,直接执行reject
try {
executor(resolve, rejecte)
} catch (err) {
console.log(err)
}
}
}

第二步:then 方法

class Promise2 {
// do something(代码如上)

constructor(executor) {
// do something(如上代码)
}
// then 方法 有两个参数onFulfilled onRejected
then(onFulfilled, onRejected) {
// 状态为fulfilled,执行onFulfilled,传入成功的值
if (this.state === 'fulfilled') {
onFulfilled(this.value)
}
// 状态为rejected,执行onRejected,传入失败的原因
if (this.state === 'rejected') {
onRejected(this.reason)
}
}
}

第三步:多个异步方法

有可能在执行 .then 方法的时候 state 还是 pending 状态, 需要把成功或者是失败的状态存到对应的数组里面

class Promise3 {
protected state!: StateType
protected value!: any
protected reason!: any

// 成功的数组
protected resolvedCallbacks!: Function[]

// 失败的数组
protected rejectedCallbacks!: Function[]

constructor(executor: Function) {
this.state = 'pending'
this.value = undefined
this.reason = undefined

const resolve = (val: any) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = val

// reslove 执行的调用成功的数组
this.resolvedCallbacks.forEach((func) => func())
}
}

const rejecte = (reason: any) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason

this.rejectedCallbacks.forEach((func) => func())
}
}

// 如果executor执行报错,直接执行reject
try {
executor(resolve, rejecte)
} catch (err) {
console.log(err)
}
}

// 需要解决异步的实现方法
then(onFulfilled, onRejected) {
// 状态为fulfilled,执行onFulfilled,传入成功的值
if (this.state === 'fulfilled') {
onFulfilled(this.value)
}
// 状态为rejected,执行onRejected,传入失败的原因
if (this.state === 'rejected') {
onRejected(this.reason)
}

if (this.state === 'pending') {
// onFulfilled传入到成功数组
this.resolvedCallbacks.push(() => {
onFulfilled(this.value)
})

// onRejected传入到失败数组
this.rejectedCallbacks.push(() => {
onRejected(this.value)
})
}
}
}

第四步:链式调用

有可能会出现 new Promise().then().then() 之类的回调写法

为了达成链式调用的效果,需要把每一层的 .then() 方法都传递当前的 promise 给下一个 .then()

按照我的理解,.then() 返回的可能是 promise 或者是 promise 返回的结果(普通值)

则给到第二个 .then() 的结果就是要返回的一个具体的 promise 结果

为了处理循环引用的情况,暂定一个 resolvePromise 方法,可以看下面的一段省略逻辑的代码

// 暂定一个处理逻辑的方法
const resolvePromise_tmp = (...args: any[]) => {}

class Promise4 {
protected state!: StateType
protected value!: any
protected reason!: any

// 成功的数组
protected resolvedCallbacks!: Function[]

// 失败的数组
protected rejectedCallbacks!: Function[]

constructor(executor: Function) {
this.state = 'pending'
this.value = undefined
this.reason = undefined

const resolve = (val: any) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = val

// reslove 执行的调用成功的数组
this.resolvedCallbacks.forEach((func) => func())
}
}

const rejecte = (reason: any) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason

this.rejectedCallbacks.forEach((func) => func())
}
}

// 如果executor执行报错,直接执行reject
try {
executor(resolve, rejecte)
} catch (err) {
console.log(err)
}
}

// 需要解决异步的实现方法
then(onFulfilled, onRejected) {
// 状态为fulfilled,执行onFulfilled,传入成功的值
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
let x = onFulfilled(this.value)
resolvePromise_tmp(promise2, resolve, reject)
}

if (this.state === 'rejected') {
let x = onRejected(this.reason)
resolvePromise_tmp(promise2, x, resolve, reject)
}

if (this.state === 'pending') {
this.resolvedCallbacks.push(() => {
let x = onFulfilled(this.value)
resolvePromise_tmp(promise2, x, resolve, reject)
})
this.rejectedCallbacks.push(() => {
let x = onRejected(this.reason)
resolvePromise_tmp(promise2, x, resolve, reject)
})
}
})

return promise2
}
}

第五步:处理最终的 resolvePromise 方法

需要注意几点:

  1. x 不可以是 null
  2. x 不能等于传进去参数 promise2 本身,不然会出现循环引用的情况
function resolvePromise(
promise2: Promise<any>,
x: any,
resolve: Function,
reject: Function
) {
// 循环引用报错
if (x === promise2) {
// reject报错
return reject(new TypeError('Chaining cycle detected for promise'))
}
// 防止多次调用
let called

// x不是null 且x是对象或者函数
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
// A+规定,声明then = x的then方法
let then = x.then
// 如果then是函数,就默认是promise了
if (typeof then === 'function') {
// 就让then执行 第一个参数是this 后面是成功的回调 和 失败的回调
then.call(
x,
(y) => {
// 成功和失败只能调用一个
if (called) return
called = true
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject)
},
(err) => {
// 成功和失败只能调用一个
if (called) return
called = true
reject(err) // 失败了就失败了
}
)
} else {
resolve(x) // 直接成功即可
}
} catch (e) {
// 也属于失败
if (called) return
called = true
// 取then出错了那就不要在继续执行了
reject(e)
}
} else {
resolve(x)
}
}

解决其他问题

比如 then 的参数 onFulfilled, onRejected 都是可选参数,如果不是返回函数的话,上面的代码处理都不成立

onFulfilled 的时候需要判断 then 的值,如果是一个普通的值 this.value = value 即可 onRejected 的时候就不可以 this.value = value 了, 如果是这样的话会传递成 onFulfilled 给下一个 then,所以需要 throw 出来

class Promise5 {
protected state!: StateType
protected value!: any
protected reason!: any

// 成功的数组
protected resolvedCallbacks!: Function[]

// 失败的数组
protected rejectedCallbacks!: Function[]

constructor(executor: Function) {
this.state = 'pending'
this.value = undefined
this.reason = undefined

const resolve = (val: any) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = val

// reslove 执行的调用成功的数组
this.resolvedCallbacks.forEach((func) => func())
}
}

const rejecte = (reason: any) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason

this.rejectedCallbacks.forEach((func) => func())
}
}

// 如果executor执行报错,直接执行reject
try {
executor(resolve, rejecte)
} catch (err) {
console.log(err)
}
}

// 需要解决异步的实现方法
then(onFulfilled, onRejected) {
// onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
onFulfilled =
typeof onFulfilled === 'function'
? onFulfilled
: (value: any) => value

// onRejected如果不是函数,就忽略onRejected,直接扔出错误
onRejected =
typeof onRejected === 'function'
? onRejected
: (err) => {
throw err
}

let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
// 异步
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
console.log(err)
}
}, 0)
}

if (this.state === 'rejected') {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
console.log(err)
}
}, 0)
}

if (this.state === 'pending') {
this.resolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
console.log(err)
}
}, 0)
})
this.rejectedCallbacks.push(() => {
// 异步
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
}
})

return promise2
}
}

第六步:其他方法

//resolve方法
Promise.resolve = function (val) {
return new Promise((resolve, reject) => {
resolve(val)
})
}

//reject方法
Promise.reject = function (val) {
return new Promise((resolve, reject) => {
reject(val)
})
}

//race方法
Promise.race = function (promises: Promise<any>[]) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject)
}
})
}

//all方法(获取所有的promise,都执行then,把结果放到数组,一起返回)
Promise.all = function (promises: Promise<any>[]) {
let arr = []
let i = 0
function processData(index, data) {
arr[index] = data
i++
if (i == promises.length) {
resolve(arr)
}
}
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then((data) => {
processData(i, data)
}, reject)
}
})
}