一个遵循PromiseA+规范的函数,解决异步回调地狱问题。
Promise主要特点
- Promise 会有三种状态,「进⾏中」「已完成」和「已拒绝」,进⾏中状态可以更改为已完成或已拒绝,已经更改过状态后⽆法继续更改(例如从已完成改为已拒绝)。
- Promise 构造之后需要传⼊⼀个函数,它接受两个参数,执⾏第⼀个参数之后就会改变当前 promise 为「已完成」状态,执⾏ 第⼆个参数之后就会变为「已拒绝」状态。
- 通过
.then
⽅法,即可在上⼀个 promise 达到已完成 时继续执⾏下⼀个函数或 promise。同时通过 resolve 或 reject 时传⼊参数,即可给下⼀个函数或 promise 传⼊初始值。
- 已拒绝的 promise,后续可以通过 .catch ⽅法或是 .then ⽅法的第⼆个参数或是 try catch 进⾏捕获。
结构梳理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class simplePromise { constructor(handleFn) { this.status = 'pending' handleFn(resolve, reject) } then() { return new simplePromise(() => { if (this.status === 'pending') { } }) } catch() {} resolve() {} reject() {} }
|
实现resolve
这是Promise的第一个关键点,主要在于逻辑的判断,如何将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
| class SimplePromise { constructor(handleFn) { this.status = 'pending' this.fulfilledList = [] handleFn(params => { }) } then(onFulfilled, onRejected) { return new SimplePromise((onNextFulfilled, onNextRejected) => { function finalFn(params) { if (typeof onFulfilled !== 'function') { onNextFulfilled(params) } else { const res = onFulfilled(params) if (res && res instanceof SimplePromise) { res.then(onNextFulfilled) } else { onNextFulfilled(res) } } } if (this.status === 'pending') { this.fulfilledList.push(finalFn) } }) } }
|
第二个关键点在于,用户传入的函数,在构造时会立即执行,但收集依赖是在then方法中,所以需要将传入的函数放到异步队列中去执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class SimplePromise { constructor(handleFn) { this.status = 'pending' this.fulfilledList = [] handleFn((val) => { setTimeout(() => { if (this.status !== 'pending') { return } this.status = 'fulfilled' this.fulfilledList.forEach(fn => fn(val)) this.fulfilledList = [] }, 0) }) } then(onFulfilled, onRejected) { ..... } catch() { } resolve() { } reject() { } }
|
- 接下来将reject也实现进去,handleFn为用户传入的函数,其接收两个参数resolve和reject,我们分别执行了两个队列里收集的任务。
- 而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
| class SimplePromise { constructor(handleFn) { this.status = 'pending' this.fulfilledList = [] this.rejectedList = [] handleFn(this.trigger.bind(this, 'fulfilled'), this.trigger.bind(this, 'rejected')) } trigger(status, val) { setTimeout(() => { if (this.status !== 'pending') return; this.status = status this[`${status}List`].forEach(fn => fn(val)) }, 0) } then(onFulfilled, onRejected) { return new SimplePromise((onNextFulfilled, onNextRejected) => { function createFinalFn(prev, next) { return function (params) { if (typeof prev !== 'function') { next(params) } else { const res = prev(params) res && res instanceof SimplePromise ? res.then(next) : next(res) } } } if (this.status === 'pending') { this.fulfilledList.push(createFinalFn(onFulfilled, onNextFulfilled)) this.rejectedList.push(createFinalFn(onRejected, onNextRejected)) } }) } catch(onRejected) { return this.then(null, onRejected) } static resolve(val) { return new SimplePromise(resolve => resolve(val)) } static reject(val) { return new SimplePromise((resolve, reject) => reject(val)) } }
|
至此大概40行的代码实现了一个简单的Promise,写个例子测试下
1 2 3 4 5 6 7 8
| const p = new SimplePromise((resolve, reject) => { resolve('success'); }).then(res => { console.log('ok', res) }, err => { console.log('no', err) })
|
本例中实现的简单Promise无法跑通全部官方测试用例,只是对实现规范中的核心部分进行一次编写练习。
最后用上SimplePromise来运行一道据说来自网易的面试题加深对Promise的认识
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| var promise = new SimplePromise(function(resolve, reject){ setTimeout(function() { resolve(1); }, 1000) })
promise.then(() => { return SimplePromise.resolve(2); }).then((n) => { console.log('第一题',n) });
promise.then(() => { return 2 }).then((n) => { console.log('第二题',n) });
promise.then(2).then((n) => { console.log('第三题',n) });
|
输出结果:
第二题 2
第三题 1
第一题 2