相信你们常常使用Promise
,或者使用Generator
、asnyc/await
等异步解决方案,网上的Promise
原理也遍地开花。
一直以来想抽出时间也写一写Promise
实现,可是日常工做也是忙的不可开交,正好元旦放了3天假期,休息了2天半,抽出半天时间来看一看Promise
。javascript
new Promise((resolve, reject) => { setTimeout(() => { resolve(1); }, 1000) }).then((data) => { console.log(data); return new Promise((res) => { setTimeout(() => { res(2); },1000) }) }).then((res) => { console.log(res); }) 复制代码
Promise
是一个包含了兼容promise
规范then
方法的对象或函数。thenable
是一个包含了then
方法的对象或函数。value
是任何Javascript
值。 (包括 undefined
, thenable
, Promise
等)。exception
是由throw
表达式抛出来的值。reason
是一个用于描述Promise
被拒绝缘由的值。因为Promise/A+
规范并不包括catch
、race
、all
等方法的实现,因此这里也不会去详细解释。java
一个Promise
必须处在其中之一的状态:pending
, fulfilled
或 rejected
。 若是是pending
状态,则promise
:git
能够转换到fulfilled
或rejected
状态。 若是是fulfilled
状态,则promise
:github
若是是rejected
状态,则promise
能够:segmentfault
function MyPromise(callback) { let that = this; //定义初始状态 //Promise状态 that.status = 'pending'; //value that.value = 'undefined'; //reason 是一个用于描述Promise被拒绝缘由的值。 that.reason = 'undefined'; //定义resolve function resolve(value) { //当status为pending时,定义Javascript值,定义其状态为fulfilled if(that.status === 'pending') { that.value = value; that.status = 'resolved'; } } //定义reject function reject(reason) { //当status为pending时,定义reason值,定义其状态为rejected if(that.status === 'pending') { that.reason = reason; that.status = 'rejected'; } } //捕获callback是否报错 try { callback(resolve, reject); } catch (error) { reject(error); } } 复制代码
Promise
对象有一个then
方法,用来注册在这个Promise
状态肯定后的回调,then
方法接受两个参数: Promise.then(onFulfilled,onRejected)
。
咱们把then函数写在原型上。数组
MyPromise.prototype.then = function(onFulfilled, onRejected) { let that = this; if(that.status === 'resolved') { onFulfilled(that.value); } if(that.status === 'rejected') { onRejected(that.reason); } } 复制代码
上述代码只是实现了Promise
的最基本逻辑,若是直接调用then
是能够执行的,可是并不支持异步,而Promise
最大的特色就是解决callback
异步回调地狱的问题。
因此咱们来改造下。promise
function MyPromise(callback) { let that = this; //定义初始状态 //Promise状态 that.status = 'pending'; //value that.value = 'undefined'; //reason 是一个用于描述Promise被拒绝缘由的值。 that.reason = 'undefined'; //用来解决异步问题的数组 that.onFullfilledArray = []; that.onRejectedArray = []; //定义resolve function resolve(value) { //当status为pending时,定义Javascript值,定义其状态为fulfilled if(that.status === 'pending') { that.value = value; that.status = 'resolved'; that.onFullfilledArray.forEach((func) => { func(that.value); }); } } //定义reject function reject(reason) { //当status为pending时,定义reason值,定义其状态为rejected if(that.status === 'pending') { that.reason = reason; that.status = 'rejected'; that.onRejectedArray.forEach((func) => { func(that.reason); }); } } //捕获callback是否报错 try { callback(resolve, reject); } catch (error) { reject(error); } } 复制代码
then
函数的改造bash
MyPromise.prototype.then = function(onFulfilled, onRejected) { let that = this; //须要修改下,解决异步问题,即当Promise调用resolve以后再调用then执行onFulfilled(that.value)。 //用两个数组保存下onFulfilledArray if(that.status === 'pending') { that.onFullfilledArray.push((value) => { onFulfilled(value); }); that.onRejectedArray.push((reason) => { onRejected(reason); }); } if(that.status === 'resolved') { onFulfilled(that.value); } if(that.status === 'rejected') { onRejected(that.reason); } } 复制代码
因为Promise/A+
规范规定一个Promise
必须处在其中之一的状态:pending
, fulfilled
或 rejected
,因此在用户使用Promise
时,写的是异步代码的话,那么此时Promise
必定是处于pending
状态,反之正常调用。
所以,初始化Promise
时,定义两个数组为onFullfilledArray
,onRejectedArray
,用来保存then
函数的两个回调函数onFulfilled
和onRejected
。同时咱们在then
函数中判断status
是不是pending
,而后将onFulfilled
和onRejected
分别传入对应数组中。当用户调用resolve
或reject
时,更改状态,遍历数组,执行onFulfilled
或者onRejected
,从而异步调用。markdown
在Promise/A+
规范中:异步
对于一个promise,它的then方法能够调用屡次
promise
fulfilled
后,全部onFulfilled
都必须按照其注册顺序执行。promise
rejected
后,全部OnRejected
都必须按照其注册顺序执行。then 必须返回一个promise
onFulfilled
或 onRejected
返回了值x
, 则执行Promise
解析流程[[Resolve]](promise2, x)
。onFulfilled
或 onRejected
抛出了异常e
, 则promise2
应当以e
为reason
被拒绝。onFulfilled
不是一个函数且promise1
已经fulfilled
,则promise2
必须以promise1
的值fulfilled
。OnReject
不是一个函数且promise1
已经rejected
, 则promise2
必须以相同的reason
被拒绝。MyPromise.prototype.then = function(onFulfilled, onRejected) { let that = this; let promise2; // 根据标准,若是then的参数不是function,则咱们须要忽略它 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) {}; onRejected = typeof onRejected === 'function' ? onRejected : function(r) {}; //须要修改下,解决异步问题,即当Promise调用resolve以后再调用then执行onFulfilled(that.value)。 //用两个数组保存下onFulfilledArray if(that.status === 'pending') { return promise2 = new Promise(function(resolve, reject) { that.onFullfilledArray.push((value) => { try { let x = onFulfilled(that.value); //判断onFulfilled是不是一个Promise,若是是,那么就直接把MyPromise中的resolve和reject传给then; //返回值是一个Promise对象,直接取它的结果作为promise2的结果 if(x instanceof MyPromise) { x.then(resolve, reject); } //不然,以它的返回值作为promise2的结果 resolve(x); } catch (error) { reject(error); } }); that.onRejectedArray.push((value) => { try { let x = onRejected(that.value); //判断onRejected是不是一个Promise,若是是,那么就直接把MyPromise中的resolve和reject传给then; //返回值是一个Promise对象,直接取它的结果作为promise2的结果 if(x instanceof MyPromise) { x.then(resolve, reject); } //不然,以它的返回值作为promise2的结果 resolve(x); } catch (error) { reject(error); } }); }) } if(that.status === 'fulfilled') { return promise2 = new MyPromise(function(resolve, reject) { try { let x = onFulfilled(that.value); //判断onFulfilled是不是一个Promise,若是是,那么就直接把MyPromise中的resolve和reject传给then; //返回值是一个Promise对象,直接取它的结果作为promise2的结果 if(x instanceof MyPromise) { x.then(resolve, reject); } //不然,以它的返回值作为promise2的结果 resolve(x); } catch (error) { reject(error); } }) } if(that.status === 'rejected') { return new MyPromise(function(resolve, reject) { try { let x = onRejected(that.value); //判断onRejected是不是一个Promise,若是是,那么就直接把MyPromise中的resolve和reject传给then; //返回值是一个Promise对象,直接取它的结果作为promise2的结果 if(x instanceof MyPromise) { x.then(resolve, reject); } //不然,以它的返回值作为promise2的结果 resolve(x); } catch (error) { reject(error); } }) } } 复制代码
在调用then
时,判断onFulfilled
和onRejected
是不是一个函数,若是不是,返回一个匿名函数,同时必须返回各参数的值,用来解决链式调用时Promise
值的穿透问题。
例如:
new MyPromise(resolve=>resolve(8)) .then() .then() .then(function foo(value) { alert(value) }) 复制代码
因此咱们把这块改为这样:
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) { return f}; onRejected = typeof onRejected === 'function' ? onRejected : function(r) {throw r}; 复制代码
可是这样每次判断都须要从新写这个x
和MyPromise
的关系,因此,咱们须要将这块代码给抽象出来,这块代码在Promise/A+
规范中叫作resolvePromise
。
function resolvePromise(promise, x, resolve, reject) { let then,thenCalledOrThrow = false //若是promise 和 x 指向相同的值, 使用 TypeError作为缘由将promise拒绝。 if (promise === x) { return reject(new TypeError('Chaining cycle detected for promise!')) } //判断x是不是一个Promise,若是是,那么就直接把MyPromise中的resolve和reject传给then; //返回值是一个Promise对象,直接取它的结果作为promise2的结果 if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) { try { then = x.then if (typeof then === 'function') { // typeof //x.then(resolve, reject); then.call(x, function rs(y) { if (thenCalledOrThrow) return thenCalledOrThrow = true return resolvePromise(promise, y, resolve, reject) }, function rj(r) { if (thenCalledOrThrow) return thenCalledOrThrow = true return reject(r) }) } else { return resolve(x) } } catch(e) { if (thenCalledOrThrow) return thenCalledOrThrow = true return reject(e) } } else { return resolve(x) } } 复制代码
then
函数最后修改成:
MyPromise.prototype.then = function(onFulfilled, onRejected) { let that = this; let promise2; // 根据标准,若是then的参数不是function,则咱们须要忽略它 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) { return f}; onRejected = typeof onRejected === 'function' ? onRejected : function(r) {throw r}; //须要修改下,解决异步问题,即当Promise调用resolve以后再调用then执行onFulfilled(that.value)。 //用两个数组保存下onFulfilledArray if(that.status === 'pending') { return promise2 = new Promise(function(resolve, reject) { that.onFullfilledArray.push((value) => { try { let x = onFulfilled(value); resolvePromise(promise2, x, resolve, reject) } catch(e) { return reject(e) } }); that.onRejectedArray.push((value) => { try { let x = onRejected(value); resolvePromise(promise2, x, resolve, reject) } catch(e) { return reject(e) } }); }) } if(that.status === 'fulfilled') { return promise2 = new MyPromise(function(resolve, reject) { try { let x = onFulfilled(that.value); //处理then的多种状况 resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error); } }) } if(that.status === 'rejected') { return new MyPromise(function(resolve, reject) { try { let x = onRejected(that.value); //处理then的多种状况 resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }) } } 复制代码
测试一下:
new MyPromise((resolve, reject) => { setTimeout(() => { resolve(1); }, 1000) }).then((data) => { console.log(data); return new MyPromise((res) => { setTimeout(() => { res(2); },1000) }) }).then((res) => { console.log(res); }) //1 //2 复制代码
Promise GITHUB地址。
参考资料:
《Promise/A+规范》
《Promise3》
《实现一个完美符合Promise/A+规范的Promise》
《剖析Promise内部结构,一步一步实现一个完整的、能经过全部Test case的Promise类》