Promise核心

起步构建

首先声明定义类并声明Promise状态与值,有以下几个细节需要注意。

  • executor为执行者
  • 当执行者出现异常时触发拒绝状态
  • 使用静态属性保存状态值
  • 状态只能改变一次,所以在resolve与reject添加条件判断
  • 因为 resolve或rejected方法在executor中调用,作用域也是executor作用域,这会造成this指向window,现在我们使用的是class定义,this为undefined。
class MK {
	static PENDING = 'pending'
	static FULFILLED = 'fulfilled'
	static REJECTED = 'rejected'

	constructor(executor) {
		this.status = MK.PENDING
		this.value = null

		try {
			executor(this.resolve.bind(this), this.reject.bind(this))
		} catch (error) {
			this.reject(error)
		}
	}

	resolve(value) {
		if (this.status === MK.PENDING) {
			this.status = MK.FULFILLED
			this.value = value
		}
	}

	reject(value) {
		if (this.status === MK.PENDING) {
			this.status = MK.REJECTED
			this.value = value
		}
	}
}

const p = new MK((resolve, reject) => {
	resolve('mkimq')
})

console.log(p)

THEN

现在添加then方法来处理状态的改变,有以下几点说明

  1. then可以有两个参数,即成功和错误时的回调函数
  2. then的函数参数都不是必须的,所以需要设置默认值为函数,用于处理当没有传递时情况
  3. 当执行then传递的函数发生异常时,统一交给onRejected来处理错误

基础构建

class MK {
	static PENDING = 'pending'
	static FULFILLED = 'fulfilled'
	static REJECTED = 'rejected'

	constructor(executor) {
		this.status = MK.PENDING
		this.value = null

		try {
			executor(this.resolve.bind(this), this.reject.bind(this))
		} catch (error) {
			this.reject(error)
		}
	}

	then(onFulfilled, onRejected) {
		if (typeof onFulfilled !== 'function') {
			onFulfilled = value => value
		}

		if (typeof onRejected != 'function') {
			onRejected = value => value
		}

		if (this.status === MK.FULFILLED) {
			try {
				onFulfilled(this.value)
			} catch (error) {
				onRejected(error)
			}
		}

		if (this.status == MK.REJECTED) {
			try {
				onRejected(this.value)
			} catch (error) {
				onRejected(error)
			}
		}
	}

	resolve(value) {
		if (this.status === MK.PENDING) {
			this.status = MK.FULFILLED
			this.value = value
		}
	}

	reject(value) {
		if (this.status === MK.PENDING) {
			this.status = MK.REJECTED
			this.value = value
		}
	}
}

new MK((resolve, reject) => {
	resolve('mkimq')
}).then(
	v => {
		console.log(v)
	},
	e => {
		console.log(e)
	}
)

异步任务

但上面的代码产生的Promise并不是异步的,使用setTimeout来将onFulfilled与onRejected做为异步宏任务执行

then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
        onFulfilled = value => value
    }

    if (typeof onRejected != 'function') {
        onRejected = value => value
    }

    if (this.status === MK.FULFILLED) {
        setTimeout(() => {
            try {
                onFulfilled(this.value)
            } catch (error) {
                onRejected(error)
            }
        })
    }

    if (this.status == MK.REJECTED) {
        setTimeout(() => {
            try {
                onRejected(this.value)
            } catch (error) {
                onRejected(error)
            }
        })
    }
}

PENDING状态

目前then方法无法处理promise为pending时的状态

const p = new MK((resolve, reject) => {
    setTimeout(() => {
        resolve('mkimq')
    })
})

为了处理以下情况,需要进行几点改动

  1. 在构造函数中添加callbacks来保存pending状态时处理函数,当状态改变时循环调用
constructor(executor) {
	...
    this.callbacks = []
    ...
}
  1. 将then方法的回调函数添加到 callbacks 数组中,用于异步执行
then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
        onFulfilled = value => value
    }

    if (typeof onRejected != 'function') {
        onRejected = value => value
    }

    if (this.status === MK.PENDING) {
        this.callbacks.push({
            onFulfilled: value => {
                try {
                    onFulfilled(value)
                } catch (error) {
                    onRejected(error)
                }
            },
            onRejected: value => {
                try {
                    onRejected(value)
                } catch (error) {
                    onRejected(error)
                }
            }
        })
    }
}
  1. resovle与reject中添加处理callback方法的代码
resolve(value) {
    if (this.status === MK.PENDING) {
        this.status = MK.FULFILLED
        this.value = value

        this.callbacks.map(callback => {
            callback.onFulfilled(value)
        })
    }
}

reject(value) {
    if (this.status === MK.PENDING) {
        this.status = MK.REJECTED
        this.value = value
        this.callbacks.map(callback => {
            callback.onRejected(value)
        })
    }
}

PENDING异步

resolve(value) {
    if (this.status === MK.PENDING) {
        this.status = MK.FULFILLED
        this.value = value
        setTimeout(() => {
            this.callbacks.map(callback => {
                callback.onFulfilled(value)
            })
        })
    }
}

reject(value) {
    if (this.status === MK.PENDING) {
        this.status = MK.REJECTED
        this.value = value
        setTimeout(() => {
            this.callbacks.map(callback => {
                callback.onRejected(value)
            })
        })
    }
}

链式操作

Promise中的then是链式调用执行的,所以then也要返回Promise才能实现

  • then的onReject函数是对前面Promise的rejected的处理
  • 但该Promise返回状态要为fulfilled,所以在调用onRejected后改变当前promise为fulfilled状态
then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
        onFulfilled = value => value
    }

    if (typeof onRejected != 'function') {
        onRejected = value => value
    }

    return new MK((resolve, reject) => {
        if (this.status === MK.PENDING) {
            this.callbacks.push({
                onFulfilled: value => {
                    try {
                        onFulfilled(value)
                    } catch (error) {
                        onRejected(error)
                    }
                },
                onRejected: value => {
                    try {
                        onRejected(value)
                    } catch (error) {
                        onRejected(error)
                    }
                }
            })
        }
    })
}

返回类型

如果then返回的是Promise呢?所以我们需要判断分别处理返回值为Promise与普通值的情况

基本实现

then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
        onFulfilled = value => value
    }

    if (typeof onRejected != 'function') {
        onRejected = value => value
    }

    return new MK((resolve, reject) => {
        if (this.status === MK.PENDING) {
            this.callbacks.push({
                onFulfilled: value => {
                    try {
                        const result = onFulfilled(value)

                        if (result instanceof MK) {
                            result.then(resolve, reject)
                        } else {
                            resolve(result)
                        }
                    } catch (error) {
                        reject(error)
                    }
                },
                onRejected: value => {
                    try {
                        const result = onRejected(value)
                        if (result instanceof MK) {
                            result.then(resolve, reject)
                        } else {
                            resolve(result)
                        }
                    } catch (error) {
                        reject(error)
                    }
                }
            })
        }

        if (this.status === MK.FULFILLED) {
            setTimeout(() => {
                try {
                    const result = onFulfilled(this.value)
                    if (result instanceof MK) {
                        result.then(resolve, reject)
                    } else {
                        resolve(result)
                    }
                } catch (error) {
                    reject(error)
                }
            })
        }

        if (this.status === MK.REJECTED) {
            setTimeout(() => {
                try {
                    const result = onRejected(this.value)
                    if (result instanceof MK) {
                        result.then(resolve, reject)
                    } else {
                        resolve(result)
                    }
                } catch (error) {
                    reject(error)
                }
            })
        }
    })
}

代码复用

现在发现pendding、fulfilled、rejected 状态的代码非常相似,所以可以提取出方法Parse来复用

then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
        onFulfilled = value => value
    }

    if (typeof onRejected != 'function') {
        onRejected = value => value
    }

    return new MK((resolve, reject) => {
        if (this.status === MK.PENDING) {
            this.callbacks.push({
                onFulfilled: value => {
                    this.parse(onFulfilled(this.value), resolve, reject)
                },
                onRejected: value => {
                    this.parse(onRejected(this.value), resolve, reject)
                }
            })
        }

        if (this.status === MK.FULFILLED) {
            setTimeout(() => {
                this.parse(onFulfilled(this.value), resolve, reject)
            })
        }

        if (this.status === MK.REJECTED) {
            setTimeout(() => {
                this.parse(onRejected(this.value), resolve, reject)
            })
        }
    })
}

parse(result, resolve, reject) {
    try {
        if (result instanceof MK) {
            result.then(resolve, reject)
        } else {
            resolve(result)
        }
    } catch (error) {
        reject(error)
    }
}

返回约束

then的返回的promise不能是then相同的Promise,下面是原生Promise的示例将产生错误

const p = new Promise(resolve => {
    resolve('mkimq')
})

const p2 = p.then(v => {
    return p2
})

解决上面的问题来完善代码,添加当前promise做为parse的第一个参数与函数结果比对

then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
        onFulfilled = value => value
    }

    if (typeof onRejected != 'function') {
        onRejected = value => value
    }

    const promise = new MK((resolve, reject) => {
        if (this.status === MK.PENDING) {
            this.callbacks.push({
                onFulfilled: value => {
                    this.parse(promise, onFulfilled(this.value), resolve, reject)
                },
                onRejected: value => {
                    this.parse(promise, onRejected(this.value), resolve, reject)
                }
            })
        }

        if (this.status === MK.FULFILLED) {
            setTimeout(() => {
                this.parse(promise, onFulfilled(this.value), resolve, reject)
            })
        }

        if (this.status === MK.REJECTED) {
            setTimeout(() => {
                this.parse(promise, onRejected(this.value), resolve, reject)
            })
        }
    })

    return promise
}

parse(promise, result, resolve, reject) {
    if (promise == result) {
        throw new TypeError('Chaining cycle detected for promise')
    }

    try {
        if (result instanceof MK) {
            result.then(resolve, reject)
        } else {
            resolve(result)
        }
    } catch (error) {
        reject(error)
    }
}

RESOLVE

实现Promise的resolve方法

static resolve(value) {
    return new MK((resolve, reject) => {
        if (value instanceof MK) {
            value.then(resolve, reject)
        } else {
            resolve(value)
        }
    })
}

REJEDCT

定义Promise的rejecte方法

static reject(value) {
    return new MK((_, reject) => {
        reject(value)
    })
}

ALL

实现Promise的all方法

static all(promises) {
    const resolves = []

    return new MK((resolve, reject) => {
        promises.forEach((promise, index) => {
            promise.then(
                value => {
                    resolves.push(value)

                    if (resolves.length === promises.length) {
                        resolve(resolves)
                    }
                },
                reason => {
                    reject(reason)
                }
            )
        })
    })
}

RACE

实现Promise的race方法

static race(promises) {
    return new MK((resolve, reject) => {
        promises.forEach(promise => {
            promise.then(value => {
                resolve(value)
            })
        })
    })
}
代码详情
class MK {
	static PENDING = 'pending'
	static FULFILLED = 'fulfilled'
	static REJECTED = 'rejected'

	constructor(executor) {
		this.status = MK.PENDING
		this.value = null
		this.callbacks = []

		try {
			executor(this.resolve.bind(this), this.reject.bind(this))
		} catch (error) {
			this.reject(error)
		}
	}

	static resolve(value) {
		return new MK((resolve, reject) => {
			if (value instanceof MK) {
				value.then(resolve, reject)
			} else {
				resolve(value)
			}
		})
	}

	static reject(value) {
		return new MK((_, reject) => {
			reject(value)
		})
	}

	static all(promises) {
		const resolves = []

		return new MK((resolve, reject) => {
			promises.forEach((promise, index) => {
				promise.then(
					value => {
						resolves.push(value)

						if (resolves.length === promises.length) {
							resolve(resolves)
						}
					},
					reason => {
						reject(reason)
					}
				)
			})
		})
	}

	static race(promises) {
		return new MK((resolve, reject) => {
			promises.forEach(promise => {
				promise.then(value => {
					resolve(value)
				})
			})
		})
	}

	then(onFulfilled, onRejected) {
		if (typeof onFulfilled !== 'function') {
			onFulfilled = value => value
		}

		if (typeof onRejected != 'function') {
			onRejected = value => value
		}

		const promise = new MK((resolve, reject) => {
			if (this.status === MK.PENDING) {
				this.callbacks.push({
					onFulfilled: value => {
						this.parse(promise, onFulfilled(this.value), resolve, reject)
					},
					onRejected: value => {
						this.parse(promise, onRejected(this.value), resolve, reject)
					}
				})
			}

			if (this.status === MK.FULFILLED) {
				setTimeout(() => {
					this.parse(promise, onFulfilled(this.value), resolve, reject)
				})
			}

			if (this.status === MK.REJECTED) {
				setTimeout(() => {
					this.parse(promise, onRejected(this.value), resolve, reject)
				})
			}
		})

		return promise
	}

	parse(promise, result, resolve, reject) {
		if (promise == result) {
			throw new TypeError('Chaining cycle detected for promise')
		}

		try {
			if (result instanceof MK) {
				result.then(resolve, reject)
			} else {
				resolve(result)
			}
		} catch (error) {
			reject(error)
		}
	}

	resolve(value) {
		if (this.status === MK.PENDING) {
			this.status = MK.FULFILLED
			this.value = value
			setTimeout(() => {
				this.callbacks.map(callback => {
					callback.onFulfilled(value)
				})
			})
		}
	}

	reject(value) {
		if (this.status === MK.PENDING) {
			this.status = MK.REJECTED
			this.value = value
			setTimeout(() => {
				this.callbacks.map(callback => {
					callback.onRejected(value)
				})
			})
		}
	}
}
贡献者: mankueng