这次我们要根据 Promise 的规范 实现一个简单的 Promise。
思路 我们从实际场景出发,从简单的应用中反推 Promise 的实现,然后根据逐步拓展的应用场景和规范,不断改进我们的 Promise。
最简单的实现 首先我们先设想一个最简单的应用场景:
1 2 var p = MyPromise((resolve ) => setTimeout (() => resolve('finish!' , 3000 ));p.then((message ) => console .log(message));
从使用场景中我们可以看出首先 Promise 的构造函数需要接收一个函数,而这个函数的参数是 resolve,然后 Promise 需要一个 then 函数来告诉 Promise 当操作结束时做什么,参照规范中的 2.2 我们这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class MyPromise { constructor (fn){ this ._onFulFilled = () => {}; this .resolve = this .resolve.bind(this ); fn(this .resolve); } resolve(message) { this ._onFulFilled(message); } then(onFulFilled){ this ._onFulFilled = onFulFilled && onFulFilled instanceof Function ? onFulFilled : this ._onFulFilled; } }
好的,现在我们初步的 Promise 完成了,但是我们发现一个问题,如果 then 里面我们要求多个参数那怎么办?如:
1 2 var p = new MyPromise((resolve ) => setTimeout (() => resolve('ok' , 'now' , 'we' , 'finish' ), 3000 ));p.then((message1, message2, message3, message4 ) => console .log(message1 + message2 + message3 + message4));
我们要利用 apply 来修改一下 onFulFilled 函数:
1 2 3 resolve(message) { this ._onFulFilled.apply(this , arguments ); }
我们要考虑到失败的情况,使用场景代码如下:
1 2 3 4 5 6 7 8 var p = new MyPromise((resolve, reject ) => { setTimeout (() => { let isExpection = Math .random() > 0.5 ; if (isExpection) reject('error!' ); else resolve('finish!' ) }, 3000 ); }); p.then((message ) => console .log(message), (message ) => console .log(message));
对应我们加上 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 MyPromise { constructor (fn) { this ._onFulFilled = () => { }; this ._onRejected = () => { }; this .resolve = this .resolve.bind(this ); this .reject = this .reject.bind(this ); fn(this .resolve, this .reject); } resolve() { this ._onFulFilled.apply(this , arguments ); } reject() { this ._onRejected.apply(this , arguments ); } then(onFulFilled, onRejected) { this ._onFulFilled = onFulFilled && onFulFilled instanceof Function ? onFulFilled : this ._onFulFilled; this ._onRejected = onRejected && onRejected instanceof Function ? onRejected : this ._onRejected; } }
实现状态判断 现在的 Promise 只能执行一次,当我们第二次执行 then 后不会返回结果,使用场景如下:
1 2 3 var p = new MyPromise((resolve ) => setTimeout (() => resolve('finish' ), 1000 ));p.then((message ) => console .log(message)); p.then((message ) => console .log(message));
首先我们要把 onFulFilled 改为一个数组,然后让 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 class MyPromise { constructor (fn) { this ._resolve_data = null ; this ._error_data = null ; this ._onFulFilleds = []; this ._onRejecteds = []; this .resolve = this .resolve.bind(this ); this .reject = this .reject.bind(this ); this .then = this .then.bind(this ); this .state = 1 ; fn(this .resolve, this .reject); } resolve() { this ._resolve_data = arguments ; this ._onFulFilleds.forEach(resolve => { resolve.apply(this , arguments ); }); this .state = 2 ; } reject() { this ._error_data = arguments ; this ._onRejecteds.forEach(reject => reject.apply(this , arguments )); this .state = -1 ; } then(onFulFilled, onRejected) { if (onFulFilled && onFulFilled instanceof Function ) { this ._onFulFilleds.push(onFulFilled); } if (onRejected && onRejected instanceof Function ) { this ._onRejecteds.push(onRejected); } switch (this .state){ case 2 : onFulFilled.apply(this , this ._resolve_data); break ; case -1 : onRejected.apply(this , this ._error_data); break ; } return this ; } }
实现链式调用 为了 Promise 用得更加美观,我们的 Promise 需要实现链式调用,其使用场景如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function nextPromise (data ) { return new MyPromise((resolve ) => setTimeout (() => resolve(data), 1000 )); } nextPromise('data' ) .then((message ) => { console .log(message + '1' ); }) .then((message ) => { console .log(message + '2' ); }) .then((message ) => { console .log(message + '3' ); }) .then((message ) => { console .log(message + '4' ); }) .then((message ) => { console .log(message + '5' ); });
我们只需要在 then 中返回 this 就行,实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 then(onFulFilled, onRejected) { if (onFulFilled && onFulFilled instanceof Function ) { this ._onFulFilleds.push(onFulFilled); } if (onRejected && onRejected instanceof Function ) { this ._onRejecteds.push(onRejected); } switch (this .state){ case 2 : onFulFilled.apply(this , this ._resolve_data); break ; case -1 : onRejected.apply(this , this ._error_data); break ; } return this ; }
实现 Promise 间的嵌套 我们使用 Promise 的一大目的是它帮我们解决了回调地狱的问题,目前我们没有处理当 then 中返回 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 function nextPromise (data ) { return new MyPromise((resolve ) => setTimeout (() => resolve(data), 1000 )); } nextPromise('1' ) .then((message ) => { console .log(message); return nextPromise('2' ) }) .then((message ) => { console .log(message); return nextPromise('3' ) }) .then((message ) => { console .log(message); return nextPromise('4' ) }) .then((message ) => { console .log(message); return nextPromise('5' ) }) .then((message ) => { console .log(message); });
这里我主要参考这篇文章 的做法,我们在 resolve 和 reject 函数进行处理,我们先判断 then 返回的是不是 Promise,是的话我们把剩余的 then 分配给返回的 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 reject() { this ._error_data = arguments ; this .state = -1 ; for (let i = 0 ; i < this ._onRejecteds.length; ++i) { let result = this ._onRejecteds[i].apply(this , arguments ); if (result instanceof MyPromise) { for (i++; i < this ._onRejecteds.length; ++i) { let onFulFilled = this ._onFulFilleds.length > i ? this ._onFulFilleds[i] : () => { }; let onRejecteds = this ._onRejecteds.length > i ? this ._onRejecteds[i] : () => { }; result.then(onFulFilled, onRejecteds); } } } } then(onFulFilled, onRejected) { if (onFulFilled && onFulFilled instanceof Function ) { this ._onFulFilleds.push(onFulFilled); } if (onRejected && onRejected instanceof Function ) { this ._onRejecteds.push(onRejected); } switch (this .state) { case 2 : onFulFilled.apply(this , this ._resolve_data); break ; case -1 : onRejected.apply(this , this ._error_data); break ; } return this ; }
总结 目前这样一个简易的 Promise 到此完成,从中我们也会发现其实写一个玩具轮子不难,难的是让这个轮子在工业中可以实际应用,源代码放在:github。