真正的手写一次promise
的实现,能更加深入理解运用promise
。 俗话说的好,知其然须知其所以然,首先要了解promise
的语法和使用。
promise
是书写异步代码的一种方式,在ES6语法之前,异步操作一般通过回调函数实现,但是异步回调往往容易造成回调地狱,挨个嵌套的问题,promise
应运而生,主要用于解决回调嵌问题,使得异步操作更为清晰。
promise
是一个对象,存在三种状态 pedning(等待中), fulfilled(成功状态), rejected(失败状态)
基本语法:
1 2 3 4 5 6 7 8 9
| const p = new Promise((resolve, reject) => {
resolve(100) })
|
使用promise
1 2 3 4 5 6 7
| p.then(res => { console.log(res) }).catch(err => { console.log(err) })
|
因为p
是由promise
生成的一个对象,由此可以想到,通过创建类来实现promise
下一步,先通过构造函数简单处理promise
的参数,对于promise
来说,传入的参数是一个函数,这个函数有包含两个函数的参数(resolve, reject
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Promise{ constructor(fn) { let resolve = () => { console.log('resolve,将状态由等待变成成功') }; let reject = () => { console.log('reject,将状态由等待变成失败') } fn(resolve, reject) } }
const p = new Promise((resolve, reject) => { resolve(); reject(); console.log('promise是立即执行的') })
|
处理了函数参数,下一步整理状态,一开始梳理的promise
得三个状态,在resolve
和reject
中需要处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| class Promise{ constructor(fn) { this.state = 'pending'; this.msg = ''; this.reason = ''; let resolve = (val) => { console.log('resolve,将状态由等待变成成功') if (this.state === 'pending') { this.state = 'fulfilled'; this.msg = val; } }; let reject = (val) => { console.log('reject,将状态由等待变成失败') if (this.state === 'pending') { this.state = 'rejected'; this.reason = val; } } fn(resolve, reject)
}
}
const p = new Promise((resolve, reject) => { resolve(2000); reject(); console.log('promise是立即执行的') })
console.log(p)
|
接下来,处理.then
即实例对象后面调用的then
,是不是可以理解为then
是原型对象promise
下的一个方法,我们调用原型下的方法而已呢? so,在promise
的class
中添加then
方法。并且确定then
方法的参数,按照es6
中对promise
的定义
Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。
其实对then
来说有两个参数,一个是成功的回调,一个是失败的回调,有点类似一开始写的reject
和resolve
,先看一个正确的promise
怎么调用then
1 2 3 4 5 6 7
| p.then(res => { console.log('成功') }, err => { console.log('失败') })
|
来尝试给类中添加then
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| class Promise{ constructor(fn) { this.state = 'pending'; this.msg = ''; this.reason = ''; let resolve = (val) => { console.log('resolve,将状态由等待变成成功') if (this.state === 'pending') { this.state = 'fulfilled'; this.msg = val; } }; let reject = (val) => { console.log('reject,将状态由等待变成失败') if (this.state === 'pending') { this.state = 'rejected'; this.reason = val; } } fn(resolve, reject) } then(fulfilledFn, rejectedFn) { if (this.state === 'fulfilled') { fulfilledFn(this.msg); } if (this.state === 'rejected') { rejectedFn(this.reason) } } }
const p = new Promise((resolve, reject) => { resolve(2000); reject(); console.log('promise是立即执行的') }) p.then(res => { console.log(res) console.log('成功') }, err => { console.log(err) console.log('失败') })
|
考虑时序问题,看下面一段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const p = new Promise((resolve, reject) => { setTimeout(() => { resolve(2000) }, 1000) console.log('promise是立即执行的') }) p.then(res => { console.log(res) console.log('成功') }, err => { console.log(err) console.log('失败') })
|
查看控制台,发现打印的内容没有进入到then
中,打印发现status
还是pending
状态,由于在promise
中resolve
是异步执行的,所以执行到then
的时候,还没有完成status
的状态变更,无法执行回调。
通过添加事件队列和判断处理事件推入队列时机(在then
中但是状态是pending
)和事件执行时机(改变status
状态的两个函数)来处理resolve
和reject
的异步事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| class Promise{ constructor(fn) { this.state = 'pending'; this.msg = ''; this.reason = '';
this.resolveArr = []; this.rejectArr = [];
let resolve = (val) => { console.log('resolve,将状态由等待变成成功') if (this.state === 'pending') { this.state = 'fulfilled'; this.msg = val; this.resolveArr.forEach((fn) => { fn(); }) } }; let reject = (val) => { console.log('reject,将状态由等待变成失败') if (this.state === 'pending') { this.state = 'rejected'; this.reason = val; this.rejectArr.forEach((fn) => { fn(); }) } } fn(resolve, reject) } then(fulfilledFn, rejectedFn) { if (this.state === 'fulfilled') { fulfilledFn(this.msg); } if (this.state === 'rejected') { rejectedFn(this.reason) } if (this.state === 'pending') { this.resolveArr.push(() => { fulfilledFn(this.msg); }); this.rejectArr.push(() => { rejectedFn(this.reason); }) } } }
const p = new Promise((resolve, reject) => { setTimeout(() => { resolve(666) }, 1000) console.log('promise是立即执行的') }) p.then(res => { console.log(res) console.log('成功') }, err => { console.log(err) console.log('失败') }) console.log(p)
|
处理完异步,接下来看一下promise
的链式调用
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
按照promise
的定义,改造我们的class
,让then
再返回promise
实现链式,这里需要注意的是,当then
里面去判断返回的是不是当前promise
本身,因为代码执行顺序关系,会报错,因为当时的promise
还没生成完毕,所以在判断过程中,添加了异步操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| class Promise{ constructor(fn) { this.state = 'pending'; this.msg = ''; this.reason = '';
this.resolveArr = []; this.rejectArr = [];
let resolve = (val) => { if (this.state === 'pending') { this.state = 'fulfilled'; this.msg = val; this.resolveArr.forEach((fn) => { fn(); }) } }; let reject = (val) => { console.log('reject,将状态由等待变成失败') if (this.state === 'pending') { this.state = 'rejected'; this.reason = val; this.rejectArr.forEach((fn) => { fn(); }) } } fn(resolve, reject)
} then(fulfilledFn, rejectedFn) { let promise2 = new Promise((resolve, reject) => { if (this.state === 'fulfilled') { setTimeout(() => { let fulfilledRes = fulfilledFn(this.msg); if (fulfilledRes === promise2) { console.error('循环调用了promise,报错') } else { if (fulfilledRes instanceof Promise) { fulfilledRes.then((msg) => { resolve(msg) }, (err) => { reject(err) }) } else { resolve(fulfilledRes) } } }, 0) } if (this.state === 'rejected') { setTimeout(() => { let rejectedRes = rejectedFn(this.msg); if (fulfilledRes === promise2) { console.log('循环了') } else { if (rejectedRes instanceof Promise) { rejectedRes.then((msg) => { resolve(msg) }, (err) => { reject(err) }) } else { reject(rejectedRes) } } }, 0) } if (this.state === 'pending') { this.resolveArr.push(() => { fulfilledFn(this.msg); }); this.rejectArr.push(() => { rejectedFn(this.reason); }) }
}) return promise2; }
} let p = new Promise((resolve, reject) => { resolve(300) }); const p2 = p.then((res) => { console.log(res) return p2 }) p2.then(res => { console.log(res) })
|
promise
类有了,接下去,实现promise
的静态方法
resolve
有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。
1 2 3 4 5
| Promise.resolve = (val) => { return new Promise((resolve, reject) => { resolve(val); }) }
|
reject
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected
1 2 3 4 5
| Promise.reject = (val) => { return new Promise((resolve, reject) => { reject(val); }) }
|
粗糙版本的promise
差不多已经实现了,还有很多方法没有挨个实现,代码也不是很严谨,这次手写promise
的过程,一点是让我重新温习巩固了关于promise
的基础知识和用法,还有一点就是手写一个已有的语法应该如何逐步去实现,逐步验证,从大范围到小细节,就好比画画,先模仿,写个大概(先写了一个class
,可以new
对象),再逐步修饰(添加status
,添加各种需要的参数)。总结下来是一次不错的经验。