前言
这一个手写专题已经有太多大佬写的很好了,为什么自己要在这里又重复写呢?
前不久在掘金上看到一位大佬这么说:学习就像爬山,每个人有每个人不同的上山方法,因此每个人也会领略到不一样的美景。
所以我把我看到的风景放在了在这里~
promise基础版
看看官方promsie的用例(也是接下来我们要率先实现的
):
let p1 = new Promise((resolve, reject) => {
resolve(1)
reject(2)
throw (3)
})
console.log(p1);
打印结果
前置知识
先补点基础知识:
- 原生的promise我们一般都会用new来创建实例
let promise = new Promise()
- 原生的promise里面可以传入
resolve
和reject
两个参数let promise = new Promise((resolve, reject) => {})
- promise有三种状态:分别是
pending
,fulfilled
和rejected
初始的时候是`pending` `pending`可以转为`fulfilled`状态,但是不能逆转 `pending`也可以转为`rejected`状态,但是也不能逆转 这里`fulfilled`和`rejected`也不能互转
手写吧
定义初始结构
// 首先创建一个myPromise类
class myPromise {
// 类的`构造函数constructor`里面添加一个参数,这里就用
// executor来做形参,并且执行一下这个参数
constructor(executor) {
this.status = 'pending' // 定义初始状态
executor();
}
}
实现 resolve 和 reject
class myPromise {
constructor(executor) {
this.status = 'pending';
this.value = null
try {
executor(this.resolve.bind(this), this.reject.bind(this))
}
catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status == 'pending') {
this.status = 'fulfilled'
this.value = value
}
}
reject(value) {
if (this.status == 'pending') {
this.status = 'reject'
this.value = value
}
}
}
let p2 = new myPromise((resolve, reject) => {
// resolve(1)
// reject(2)
throw ('1')
})
console.log(p2);
- 那么在执行
resolve()
的时候就需要判断状态是否为待定 pending
,如果是待定 pending
的话就把状态改为成功 fulfilled
;在执行reject()
的时候也是一样。 throw
的效果和reject()
一样,所以只需要在executor外层加上try catch
语句
this指向问题
可以看到这里加了bind
的
executor(this.resolve.bind(this), this.reject.bind(this))
如果我们不加bind
executor(this.resolve, this.reject)
因为这里this
是在executor里面的,并且executor又是在constructor里执行的,所以this
是指向constructor
.
在这里我们就可以使用bind
来绑定this
,只需要在构造函数constructor
中的this.resolve
和this.reject
后加上,.bind(this)
就可以了
then方法
其实最难的就是实现then
方法了。
先来看看then在Promise中是怎么使用的吧
- 基础版
let p1 = new Promise((resolve, reject) => {
resolve(1)
})
p1.then(res => console.log(res), err => console.log(err))
// 1
- 升级版
then嵌套
let p1 = new Promise((resolve, reject) => {
resolve(1)
})
p1.then(res => 2 * res, err => console.log(err))
.then(res => console.log(res), err => console.log(err))
// 2
前置知识
- then 可以传入两个参数
- 一个是
onFulfilled
表示“当状态为成功时”
- 另一个是
onRejected
表示“当状态为拒绝时”
- 一个是
- then 方法之间可以嵌套,所以说明了
then
方法会返回一个promise
值
手写吧
简单实现then嵌套
所以我们可以在then
方法里返回一个promise
对象,并且存储每次onFulfilled
的返回值即可实现一个简单的then
嵌套了。
class myPromise {
constructor(executor) {
this.status = 'pending'
this.value = null;
executor(this.resolve.bind(this), this.reject.bind(this))
}
resolve(value) {
if (this.status == 'pending') {
this.status = 'fulfilled'
this.value = value
}
}
reject(value) {
if (this.status == 'pending') {
this.status = 'reject'
this.value = value
}
}
then(onFulfilled, onRejected) {
var thenPromise = new myPromise((resolve, reject) => {
let x;
if (this.status == 'fulfilled') {
x = onFulfilled(this.value)
resolve(x)
} else if (this.status == 'reject') {
x = onRejected(this.value)
reject(x)
}
})
return thenPromise
}
}
测试用例:
let p2 = new myPromise((resolve, reject) => {
resolve(1)
reject(10)
})
p2.then(res => 2 * res, err => err * 2)
.then(res => console.log(res), err => console.log(err))
// 2
then参数返回值的讨论
当然我们知道,onFulfilled
的返回值不止这一种情况。
所以我们还得分情况讨论
- 返回值是
myPromise自身
let p2 = new myPromise((resolve, reject) => {
resolve(1)
})
p2.then(res => p2, err => err * 2)
要作为错误抛出
- 返回值是
myPromise的实例
let p2 = new myPromise((resolve, reject) => {
resolve(1)
})
p2.then(res => new myPromise((resolve, reject) => resolve(3 * res)))
- 返回值是普通的变量
let p2 = new myPromise((resolve, reject) => {
resolve(1)
})
p2.then(res => 2* res)
所以我们可以将代码改成如下形式
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : val => val
onRejected = typeof onRejected == 'function' ? onRejected : val => { throw val }
var thenPromise = new myPromise((resolve, reject) => {
// 用resolvePromise函数封装
const resolvePromise = callback => {
try {
// 拿到onFulfilled, onRejected的返回值,进行后续返回值的判断
const x = callback(this.value)
// 返回值为本身
if (x == thenPromise) {
throw new Error('不能放回自身..')
}
// 返回值为实例对象
if (x instanceof myPromise) {
x.then(resolve, reject) // 为了执行括号里的函数
}
else {
// 返回值为普通变量
resolve(x) // 改变状态和传参 为了执行函数结果
}
} catch (error) {
reject(error)
throw new Error(error)
}
}
if (this.status == 'fulfilled') {
resolvePromise(onFulfilled)
} else if (this.status == 'reject') {
resolvePromise(onRejected)
}
})
return thenPromise
}
处理异步
我们最常见的功能已经实现了,现在来处理一些重要的细节。
myPromise
中添加定时器
let p2 = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
p2.then(res => 2 * res)
then
是微任务,所以要在同步代码之后执行
let p2 = new myPromise((resolve, reject) => {
resolve(100)
}).then(res => 3 * res)
.then(res => console.log(res))
console.log(1);
// 1
// 300
- 以及我们要对then里传入的参数进行一个类型的检测
onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : val => val
onRejected = typeof onRejected == 'function' ? onRejected : val => { throw val }
最终版promise.then
代码就呼之欲出了~
class myPromise {
constructor(executor) {
this.status = 'pending';
this.value = null;
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
try {
executor(this.resolve.bind(this), this.reject.bind(this))
}
catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status == 'pending') {
this.status = 'fulfilled'
this.value = value
}
// 调用then里面的回调
while (this.onFulfilledCallbacks.length) {
this.onFulfilledCallbacks.shift()(this.value)
}
}
reject(value) {
if (this.status == 'pending') {
this.status = 'reject'
this.value = value
}
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(this.value)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : val => val
onRejected = typeof onRejected == 'function' ? onRejected : val => { throw val }
var thenPromise = new myPromise((resolve, reject) => {
const resolvePromise = callback => {
// 简单地用宏任务来实现then的微任务功能,不太严谨,但功能可以实现
setTimeout(() => {
try {
const x = callback(this.value) // onFulfilled, onRejected的返回值
if (x == thenPromise) {
throw new Error('不能放回自身..')
}
if (x instanceof myPromise) {
x.then(resolve, reject)
}
else {
resolve(x) // 改变状态和传参
}
} catch (error) {
reject(error)
throw new Error(error)
}
})
}
if (this.status == 'fulfilled') {
resolvePromise(onFulfilled) // 判断onFulfilled返回值判断
}
else if (this.status == 'reject') {
resolvePromise(onRejected)
}
else if (this.status == 'pending') {
// 如果myPromise中存在定时器,此时this.status还是pending状态
// 应该放入回调函数中储存起来
this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))
this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))
}
})
return thenPromise
}
}
参考文档
本文正在参加「金石计划 . 瓜分6万现金大奖」
手把手一行一行代码教你“手写Promise“,完美通过 Promises/A+ 官方872个测试用例 - 掘金 (juejin.cn)