本文适用于NodeJsv12和2019年11月19日最新版Chrome,写这篇文章的原因是写单元测试时,newPromise((resolve,reject)=>reject(1)).then().catch(err=>{console.log(err)})asyncfunctionjestTest(){awaitPromise.resolve().then()console.log('Thecatch预计此时已经被调用,并输出log')}jestTest()不能使用await来阻塞测试代码,正好到catch在EventLoop中被调用后的时序,从而检测执行情况抓住并通过测试。使用“魔法”这个词是因为在调用的promsie链中确实有很多默认处理程序和隐式传递值。Promise链调用为了不浪费大家的时间,我们先来看一个例子:Promise.resolve('promise1').then(res=>{console.log('promise1-1then')}).then(res=>{console.log('promise1-2then')}).then(res=>{console.log('promise1-3then')}).then(res=>{console.log('promise1-4then')})Promise.resolve('promise2').then(res=>{console.log('promise2-1then')thrownewError('mockerror1')}).then(res=>{console.log('promise2-2then')thrownewError('mockerror2')}).catch(err=>{console.log(err)})如果你用下面是一样的,那你可以跳过这篇文章:promise1-1thenpromise2-1thenpromise1-2thenpromise1-3thenError:mockerror1promise1-4then首先有一个前提,就是你已经知道这两个的thenpromises调用被跨入栈中(从前三行的输出也可以看出来)。如果你对这部分不太清楚,可以参考EventLoop的相关文章。同时需要注意的是,在文中指定的版本中,Chrome和NodeJs的EventLoop机制已经是一样的了。MDN的错误,让我们看一下原始的(我做了修改)MDN对catch的描述:基本上,如果有异常,promise链就会停止,而是在链中查找catch处理程序。链式调用会在异常发生时停止,寻找链上的catch语句执行。我最初的误解是一样的。我误以为catch会直接捕获第一个抛出的Error,也就是在promise1-2之后会输出Error,也就是那么promise2-2所在的地方就不会加入调用栈了。但是通过观察实际的输出结果,发现并不是这样的,所以可以解释为MDN解释的字面意思应该是错误的。链式调用并没有停止,而是执行了一些我们没有看到的事情。链式默认处理这时候我们需要知道then的一个默认处理,也可以直接参考MDN的描述:如果调用then的Promise采用了then没有handler的状态(fulfillment或rejection),创建一个没有额外处理程序的新Promise,只需采用随后调用的原始Promise的最终状态。如果你的promise的then缺少state处理对应的回调,then会自动生成一个接受promise状态的promise,即then会返回一个引用相同promsie的state,供后续调用。那么上面代码中的第二个promise部分就相当于Promise.resolve('promise2').then(res=>{console.log('promise2-1then')thrownewError('mockerror1')}).then(res=>{console.log('promise2-2then')thrownewError('mockerror2')//注意这个onRejected},(err)=>{returnPromise.reject(err)}).catch(err=>{console.log(err)})表示在输出结果的promise1-2和promise1-3之间,执行了promise2-2所在的then,也就是chaincall没有'直接stop,然后把promise2-2所在的地方加入到调用栈中。catch的不是直接catch的第一个then抛出的错误,而是这个隐藏的onRejected返回的相同状态的promise。Shorthand类似的,我们需要知道的是,catch(onRejected)是then(undefined,onRejected)的简写,也就是说,即使调用链的前一次调用没有出错,catch也会进入调用栈,而不是跳过直接地。Promise.resolve('promise1').then(res=>{console.log('promise1-1then')}).then(res=>{console.log('promise1-2then')}).then(res=>{console.log('promise1-3then')})Promise.resolve('promise2').then(res=>{console.log('promise2-1then')}).catch(err=>{console.log(err)}).then(res=>{console.log('其实我是promise2-3then')})asyncawait首先要注意的是在NodeJs和Chrome版本文章中指定,f(awaitpromise)完全等同于promise.then(f)。当然,在讨论promises的时候,我们不能忽略asyncawait。虽然在promise状态为onResolve时两者的处理逻辑是一样的,但是错误处理的执行逻辑是不同的。当asyncawait出错时,直接跳过后续await的执行是真的。constpromiseReject=newPromise((resolve,reject)=>{reject(newError('Error'))})constpromiseResolve1=newPromise((resolve,reject)=>{resolve('correct')})constpromiseResolve2=newPromise((resolve,reject)=>{resolve('correct')})constpromiseResolve3=newPromise((resolve,reject)=>{resolve('correct')})functiondemo1(){promiseReject.then(()=>{console.log('1-1')}).catch(err=>{console.log('1-2')})}asyncfunctiondemo2(){try{awaitpromiseRejectawaitpromiseResolve1awaitpromiseResolve2awaitpromiseResolve3}catch(error){console.log('2-1')}}//2-1//Endof1-2虽然这种执行时机几乎没有机会影响实际代码,还是希望对大家的好奇和异步代码单元测试有所帮助。
