今天,我带着你们一步一步跟着规范实现一个本身的Promise
,你们能够对照个人第二篇文章Promise介绍--规范篇或官方规范来一一学习。git
Promise
内部有三个固定的状态,咱们在文件中提早定义。github
const PENDING = "pending" const FULFILLED = "fulfilled" const REJECTED = "rejected"
首先,是构造器constructor
。segmentfault
constructor(resolver){ this._status = PENDING; // 保存内部的状态 this._result = undefined; // 保存promise对象fulfill或reject的最终结果 this._childArr = []; // 调用then方法建立的子promise对象 this._fulfillArr = []; // 调用then方法添加的onFulfilled方法 this._rejectArr = []; // 调用then方法添加的onRejected方法 if (resolver == noop) { return}; // then方法内部建立promise时间使用 // 若是resolver不是函数,则抛出TypeError错误 if (!isFunction(resolver)) { throw new TypeError('参数必须为function'); }; // 若是直接调用Promise而非经过new关键词建立,一样抛出TypeError错误 if (this instanceof Promise) { initPromise(this, resolver) } else { throw new TypeError('Promise不能够直接做为函数调用') } }
当参数传递正确时,才真正初始化Promise
对象,我提到了一个单独的函数initPromise
中,以下:数组
function initPromise(promise, resolver){ // 当调用传入的resolver函数抛出异常,则reject当前promise try { resolver(function(value){ // 封装的内部函数,处理resolve(value),也就是Promise Resolution Procedure resolve(promise, value); }, function(reason){ // 封装的内部函数,处理reject(reason) reject(promise, reason); }); } catch (e) { reject(promise, e); } }
当咱们执行new Promise((resolve){resolve(5)})
时,会走resolve(promise, value)
。接下来咱们实现一下Promise Resolution Procedure
。promise
function resolve(promise, value){ // 2.3.1 若是promise和value指向同一对象 if (promise === value) { reject(promise, new TypeError('不能够resolve Promise实例自己')) // 2.3.2 若是value是一个promise } else if (value instanceof Promise) { // 2.3.2.2 若是value处于fulfilled状态,则使用相同的value值fulfill promise。 if (value._status == FULFILLED) { fulfill(promise, value._result); // 2.3.2.3 若是value处于rejected状态,则使用相同的reason值reject promise。 } else if (value._status == REJECTED) { reject(promise, value._result); // 2.3.2.1 若是value处于pending状态,则promise一样pending并直到value状态改变。 // 从新把resolve(promise, value)添加到队列,asyncCall封装了一下异步调用 } else { asyncCall(resolve, [promise, value]); } // 2.3.3 若是x是一个object或function } else if (isObjectOrFunction(value)){ // 2.3.3.2 若是获取value.then的值时抛出异常,则经过该异常reject promise try{ let then = value.then; // 2.3.3.1 使then等于value.then // 2.3.3.3 若是then是一个函数 if (isFunction(then)) { try{ handleThenable(promise, value, then); } catch (e) { reject(promise, e); } // 2.3.3.4 若是then不是一个函数 } else { fulfill(promise, value); } } catch (e) { reject(promise, e); } // 2.3.4 value不是对象或函数 } else { fulfill(promise, value); } }
由于value.then
是函数时,处理状况一样不少且比较杂乱,我单独把这部分处理提取到handleThenable
函数中。实现以下:异步
function handleThenable(promise, value, then){ let settled = false; // 是否fulfilled或rejected try { // 2.3.3.3 若是then是一个函数,则把value做为函数中this指向来调用它 then.call(value, (otherValue)=>{ // 2.3.3.3.3 if (settled) { return}; // 2.3.3.3.1 若是resolvePromise经过传入y来调用,则执行resolve(promise, y) resolve(promise, otherValue); settled = true; }, (reason)=>{ // 2.3.3.3.3 if (settled) { return}; // 2.3.3.3.2 若是rejectPromise 经过传入缘由r来调用,则传入r来reject promise reject(promise, reason); settled = true; }) // 2.3.3.3.4 若是调用then抛出异常e } catch (e) { // 2.3.3.3.4.1 若是resolvePromise或rejectPromise已经调用,则忽略 if (settled) { return}; settled = true; // 2.3.3.3.4.2 不然,则传入e来reject promise reject(promise, e) } }
以上,基本就是完整的我对Promise Resolution Procedure
的实现。
还有一个很是重要的方法就是then
方法,接下来咱们看一下它是如何实现的。then
内部整体上分为两种状况,一种是当前promise
对象状态已经变为fulfilled
或rejected
,此时则直接把响应的回调函数添加到异步队列中,另外一种状况是当前promise
对象状态仍是pending
,此时则把响应的回调函数依次添加到数组中。async
then(onFulfilled, onRejected){ let child = new Promise(noop); // 若是当前对象状态已经改变,则直接根据状态调用响应的回调 if (this._status !== PENDING) { if (this._status == FULFILLED) { // 2.2.4 异步调用 asyncCall(()=>{ dealThen(this, child, onFulfilled); }) } else { // 2.2.4 异步调用 asyncCall(()=>{ dealThen(this, child, onRejected); }) } // 若是当前对象处于pending状态,则把onFulfilled, onRejected添加到 } else { this._childArr.push(child); this._fulfillArr.push(onFulfilled); this._rejectArr.push(onRejected); } // 返回一个新的promise对象 return child; }
具体处理逻辑我放到了一个新的函数dealThen
中,注意它是异步调用的。因此用asyncCall
方法包装了一下。函数
// 处理then function dealThen(promise, child, x){ // onFulfilled或onRejected是一个函数 if (isFunction(x)) { // 2.2.7.1 若是onFulfilled或onRejected返回了一个值value,则执行resolve(child, value) try { resolve(child, x(promise._result)); // 2.2.7.2 若是onFulfilled或onRejected抛出了异常e,则reject child并传入缘由e } catch (e) { reject(child, e); } } else { try{ // 2.2.1.1 若是onFulfilled不是一个函数,则忽略 if (promise._status == FULFILLED) { fulfill(child, promise._result); // 2.2.1.2 若是onRejected不是一个函数,则忽略 } else { reject(child, promise._result); } } catch (e) { reject(child, e); } } }
从上面的代码中咱们看到有两个比较重要的方法——fulfill
和reject
,它们才是真正改变promise
状态并调用相应回调的地方。oop
function fulfill(promise, value){ // 若是状态已经不是pending,则直接return if (promise._status !== PENDING) { return }; // 设置状态为fulfilled,并设置最终结果 promise._status = FULFILLED; promise._result = value; // 异步依次调用添加的onFulfilled方法 if (promise._fulfillArr.length > 0) { // 2.2.6.1 若是promise fulfilled,则全部的onFulfilled回调函数按照它们添加的顺序依次调用。 promise._fulfillArr.forEach((k,index)=>{ // 2.2.5 onFulfilled和onRejected必须做为函数来调用,没有this值 asyncCall(dealThen, [promise, promise._childArr[index], k]) }); } } function reject(promise, reason){ // 若是状态已经不是pending,则直接return if (promise._status !== PENDING) { return }; // 设置状态为rejected,并设置最终结果 promise._status = REJECTED; promise._result = reason; // 异步依次调用添加的onRejected方法 if (promise._rejectArr.length > 0) { // 2.2.6.2 若是promise rejected,则全部的onRejected回调函数按照它们添加的顺序依次调用。 promise._rejectArr.forEach((k,index)=>{ // 2.2.5 onFulfilled和onRejected必须做为函数来调用,没有this值 asyncCall(dealThen, [promise, promise._childArr[index], k]) }); } }
固然,还有一个实例方法catch
,其实它调用的也是then
方法。学习
catch(onRejected){ return this.then(undefined, onRejected); }
固然,Promise
还有四个实例方法,分别以下:
resolve
Promise.resolve = function(value){ return new Promise(function(resolve){resolve(value)}) }
reject
Promise.reject = function(reason){ return new Promise(function(resolve, reject){reject(reason)}) }
all和race的实现没有太好好思考,也没有跑测试,不知道有没有问题,并且我的感受实现的也比较挫,是用setInterval
一直轮询查看每个promise
实例状态有没有改变,因此就不show code了。主要内容仍是讲Promises/A+
的实现。
完整的代码见个人github。
固然也有几个问题,异步我是用setTimeout
实现的,它属于macro-task
,而原生的Promise
属于micro-task
,因此这里还有待改进。
另外,在上面的实现中,咱们发现resolve(promise, value)
中,在对2.3.2.1 若是value处于pending状态,则promise一样pending并直到value状态改变。我基本采用的也是setTimeout
轮询的方式实现的。以后看了es-promise
的实现,由于根据2.3.2.1此时promise
的状态和value
是同步的,因此能够把resolve
和reject
promise
分别放在value
相应状态的回调中,并假设此时与之对应的value
的child
是undefined
。以下所示:
// asyncCall(resolve, [promise, value]); value._childArr.push(undefined); value._fulfillArr.push((value)=>{resolve(promise, value)}); // ① value._rejectArr.push((reason)=>{reject(promise, reason)}); // ②
此时咱们还须要对fulfill
和reject
两个方法稍做改动。
function fulfill(promise, value){ if (promise._status !== PENDING) { return }; promise._status = FULFILLED; promise._result = value; if (promise._fulfillArr.length > 0) { promise._fulfillArr.forEach((k,index)=>{ // 若是对应的child不是undefined,则异步调用回调 if (promise._childArr[index]) { asyncCall(dealThen, [promise, promise._childArr[index], k]) // 若是对应的child是undefined,则直接执行回调函数①并传入value } else { k(value); } }); } } function reject(promise, reason){ if (promise._status !== PENDING) { return }; promise._status = REJECTED; promise._result = reason; if (promise._rejectArr.length > 0) { promise._rejectArr.forEach((k,index)=>{ // 若是对应的child不是undefined,则异步调用回调 if (promise._childArr[index]) { asyncCall(dealThen, [promise, promise._childArr[index], k]) // 若是对应的child是undefined,则直接执行回调函数②并传入reason } else { k(reason); } }); }; }
修改版见Promise1。
错误或不足之处,欢迎指正。