You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
classES6Promise{// 省略重复代码...then(onResolved,onRejected){letself=this;letpromise2=newES6Promise((resolve,reject)=>{letscheduleFn=()=>{setTimeout(()=>{onResolved=typeofonResolved==='function' ? onResolved : v=>v;onRejected=typeofonRejected==='function' ? onRejected : v=>{throwv};try{// 修改这里letx=self._state===State.resolved ? onResolved(self._value) : onRejected(self._value);resolveProcedure({ resolve, reject },x);}catch(e){reject(e);}});}if(this._state===State.pending){this._callbacks.push(scheduleFn);}else{scheduleFn();}});returnpromise2;}}// 根据 x 值,解析 promise 状态 resolveProcedure(promise, x)functionresolveProcedure({ resolve, reject, promise2 },x){// 2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.if(promise2===x){reject(newTypeError(x));}if(xinstanceofES6Promise){// 2.3.2 If x is a promise, adopt its statex.then(value=>resolveProcedure({resolve, reject, promise2},value),reason=>reject(reason));}elseif((typeofx==='object'&&x!==null)||(typeofx==='function')){// 2.3.3 letresolvedOrRejected=false;try{letthen=x.then;// 2.3.3.1 Let then be x.thenif(typeofthen==='function'){// 2.3.3 If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where:then.call(x,value=>{if(!resolvedOrRejected){resolveProcedure({ resolve, reject, promise2 },value);// 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).resolvedOrRejected=true;}// 2.3.3.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.},reason=>{if(!resolvedOrRejected){reject(reason);// 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r.resolvedOrRejected=true;}// 2.3.3.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.});}else{// 2.3.3.4 If then is not a function, fulfill promise with x.resolve(x);}}catch(e){if(!resolvedOrRejected){// 2.3.3.2 If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.// 2.3.3.4 If calling then throws an exception ereject(e);}}}else{resolve(x);// 2.3.4 If x is not an object or function, fulfill promise with x.}}
前言
为了增强对 ES6
Promise
工作方式的理解,我实现了一个自己的ES6Promise
类,接口与 ES6 的Promise
类一致(包含构造函数、then
、catch
、resolve
、reject
), 并且符合 Promise/A+ 规范(通过了规范的全部测试用例) 。下面从简单开始,一步步实现一个自己的Promise
。下面是最终实现的
ES6Promise
使用示例:创建 Promise
回想 ES6 的
Promise
对象构造函数,它要求传入一个执行器,执行器有两个参数resolve
和reject
,两个参数都是函数类型,我们可以在执行器中调用这两个方法,将Promise
变为resolved
或rejected
。先实现这个一步,代码如下:使用:
首先,在
MyPromise
构造函数中声明了两个成员变量status
和data
,分别表示MyPromise
的状态和数据。根据Promise
规范,其状态只能有三种:pending
、resolved
和rejected
,初始状态是pending
,一旦变化为resolved
或rejected
状态后,其状态不再变化。所以,初始设置status
为undefined
,当调用resolve()
或reject()
时,内部先判断其状态,如果状态已经发生变化,则直接返回,这样保证其状态不会被修改。否则,将状态标记为resolved
或rejected
,同时保存数据。实现 then 方法
Promise
对象可以链式调用then()
方法,这得益于then()
返回的也是Promise
对象(准确说是thenable
对象,即包含then
方法的对象),例如:所以下面
ES6Promise
的then
函数实现中,首先创建并返回一个新的ES6Promise
对象promise2
,在传入promise2
构造函数的执行器内部,通过resolve
和reject
方法修改promise2
的状态。promise2
状态何时变化,取决于当前promise1
的状态,如果promise1
状态是pending
,则等待promise1
被resolved
或rejected
时执行scheduleFn()
,否则立即执行scheduleFn()
。scheduleFn()
方法主要工作是,根据promise1
当前状态是resolved
(或rejected
),调用then(onResolved, onRejected)
方法参数中的onResolved(promise1.value)
(或onRejected(promise1.value)
),以promise1
的内部值作为参数,返回结果传递给promise2
的resolve
(或reject
)方法,从而改变promise2
的状态和内部值。按 Promise/A+ 规范scheduleFn()
必须是异步执行的,所以这里通过setTimeout()
方法,让其在下个事件循环中处理。实现 then 方法 v2
上面实现的
then()
方法中, 直接将onResolved()
(或onRejected()
)的返回值,传递给resolve
(或reject
),改变promise2
的状态和内部值。这里有一个问题,如果onResolved()
(或onRejected()
)返回的也是一个Promise
对象(或thenable
对象),那么promise2
不会等到这个返回的Promise
对象resolved
或的rejected
后才执行,而是将返回的Promise
对象作为promise2
的内部值。看下面例子,最后一个then()
方法执行后应该输出2
才符合预期,而实际输出的是ES6Promise
对象实例:为了解决上面这个问题,需要对
onResolved()
(或onRejected()
)的返回值(暂称之为x
)进行判断和处理,这里引入一个resolveProcedure()
方法,该方法根据x
值的类型,决定何时调用promise2
的resolve
或reject
方法。如果x
是一个thenable
对象,则等到该thenable
对象状态确定时才调用调用promise2
的resolve
或reject
方法,否则立即调用promise2
的resolve
,如果中间抛出异常,则立即调用promise2
的reject
方法。代码如下:修改后,再次运行上面测试代码,结果符合预期:
实现 catch 方法
ES6Promise
核心的then
方法上面已经实现,catch
方法不过是then
方法的一种便捷形式,其实现如下:实现 resolve/reject 静态
ES6 的
Promise
对象还提供了两个静态方法Promise.resolve
和Promise.reject
,通过这两个方法可以很方便的将一般javascript
值封装成Promise
对象。实现这两个方法也很简单,以Promise.resolve
为例,首先这个方法要返回一个新的Promise
对象,新的Promise
对象解析传入的值,这个解析过程交由resolveProcedure()
方法完成,由于这是resolve
方法,所以即使value
是一个被rejected
的Promise
,也要将其结果resolve
,所以传递给resolveProcedure()
方法的第一个参数都是resolve
方法。Promise.reject
方法实现类似,代码如下:测试
上面的
ES6Promsie
通过了 promises-tests 提供的全部测试用例,意味着其完全符合了 Promise/A+ 规范。可以通过 npm 安装后,查看源码和测试结果:
小结
Promise
作为社区产物,最终被纳入 ECMAScript 规范,可见其是被大众所接受的。Promise
改变了长久以来通过callback
编写异步代码的方式,让异步回调以一种更优雅的方式链式调用,并拥有更清晰的错误处理。Promise
同时也为generator/yield
和async/await
以同步方式编写异步代码提供了基础设施。Promise
使用起来很简单,但是涉及到一些复杂或极端的例子,需要对Promise
规范理解透彻才能正确得到结果。最后附上项目地址和仓库地址:
github 地址 : https://github.com/whinc/es6-promise
npm 地址:https://www.npmjs.com/package/whinc-es6-promise
参考
The text was updated successfully, but these errors were encountered: