ES6新特性Promise異步調(diào)用

2018-6-27    seo達(dá)人

如果您想訂閱本博客內(nèi)容,每天自動(dòng)發(fā)到您的郵箱中, 請(qǐng)點(diǎn)這里

Promise 的含義

Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案–回調(diào)函數(shù)和事件--更合理和更強(qiáng)大。它由社區(qū)最早提出和實(shí)現(xiàn),ES6將其寫(xiě)進(jìn)了語(yǔ)言標(biāo)準(zhǔn),統(tǒng)一了語(yǔ)法,原生提供了Promise

所謂Promise ,簡(jiǎn)單說(shuō)就是一個(gè)容器,里面保存著某個(gè)未來(lái)才回結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果。從語(yǔ)法上說(shuō),Promise是一個(gè)對(duì)象,從它可以獲取異步操作的消息。 
Promise 對(duì)象的狀態(tài)不受外界影響

三種狀態(tài):

  • pending:進(jìn)行中
  • fulfilled :已經(jīng)成功
  • rejected 已經(jīng)失敗

狀態(tài)改變: 
Promise對(duì)象的狀態(tài)改變,只有兩種可能:

  • 從pending變?yōu)閒ulfilled
  • 從pending變?yōu)閞ejected。

這兩種情況只要發(fā)生,狀態(tài)就凝固了,不會(huì)再變了,這時(shí)就稱為resolved(已定型

基本用法

ES6規(guī)定,Promise對(duì)象是一個(gè)構(gòu)造函數(shù),用來(lái)生成Promise實(shí)例


    
  1. const promist = new Promise(function(resolve,reject){
  2. if(/*異步操作成功*/){
  3. resolve(value);
  4. }else{
  5. reject(error);
  6. }
  7. })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

resolve函數(shù)的作用是,將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?nbsp;pending 變?yōu)?nbsp;resolved),在異步操作成功時(shí)調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去; 
reject函數(shù)的作用是,將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?nbsp;pending 變?yōu)?nbsp;rejected),在異步操作失敗時(shí)調(diào)用,并將異步操作報(bào)出的錯(cuò)誤,作為參數(shù)傳遞出去。

Promise的源碼分析:

1.回調(diào)地獄

曾幾何時(shí),我們的代碼是這樣的,為了拿到回調(diào)的結(jié)果,不得不callback hell,這種環(huán)環(huán)相扣的代碼可以說(shuō)是相當(dāng)惡心了


            
  1. let fs = require('fs')
  2. fs.readFile('./a.txt','utf8',function(err,data){
  3. fs.readFile(data,'utf8',function(err,data){
  4. fs.readFile(data,'utf8',function(err,data){
  5. console.log(data)
  6. })
  7. })
  8. })

終于,我們的蓋世英雄出現(xiàn)了,他身披金甲圣衣、駕著七彩祥云。好吧打岔兒了,沒(méi)錯(cuò)他就是我們的Promise,那讓我們來(lái)看看用了Promise之后,上面的代碼會(huì)變成什么樣吧


            
  1. let fs = require('fs')
  2. function read(url){
  3. return new Promise((resolve,reject)=>{
  4. fs.readFile(url,'utf8',function(error,data){
  5. error && reject(error)
  6. resolve(data)
  7. })
  8. })
  9. }
  10. read('./a.txt').then(data=>{
  11. return read(data)
  12. }).then(data=>{
  13. return read(data)
  14. }).then(data=>{
  15. console.log(data)
  16. })

2.重點(diǎn)開(kāi)始,小眼睛都看過(guò)來(lái)

2.1 Promise/A+

首先我們要知道自己手寫(xiě)一個(gè)Promise,應(yīng)該怎么去寫(xiě),誰(shuí)來(lái)告訴我們?cè)趺磳?xiě),需要遵循什么樣的規(guī)則。當(dāng)然這些你都不用擔(dān)心,其實(shí)業(yè)界都是通過(guò)一個(gè)規(guī)則指標(biāo)來(lái)生產(chǎn)Promise的。讓我們來(lái)看看是什么東西。傳送門(mén)?Promise/A+

2.2 constructor

我們先聲明一個(gè)類,叫做Promise,里面是構(gòu)造函數(shù)。如果es6還有問(wèn)題的可以去阮大大的博客上學(xué)習(xí)一下(傳送門(mén)?es6


            
  1. class Promise{
  2. constructor(executor){
  3. //控制狀態(tài),使用了一次之后,接下來(lái)的都不被使用
  4. this.status = 'pendding'
  5. this.value = undefined
  6. this.reason = undefined
  7. //定義resolve函數(shù)
  8. let resolve = (data)=>{
  9. //這里pendding,主要是為了防止executor中調(diào)用了兩次resovle或reject方法,而我們只調(diào)用一次
  10. if(this.status==='pendding'){
  11. this.status = 'resolve'
  12. this.value = data
  13. }
  14. }
  15. //定義reject函數(shù)
  16. let reject = (data)=>{
  17. if(this.status==='pendding'){
  18. this.status = 'reject'
  19. this.reason = data
  20. }
  21. }
  22. //executor方法可能會(huì)拋出異常,需要捕獲
  23. try{
  24. //將resolve和reject函數(shù)給使用者
  25. executor(resolve,reject)
  26. }catch(e){
  27. //如果在函數(shù)中拋出異常則將它注入reject中
  28. reject(e)
  29. }
  30. }
  31. }

那么接下來(lái)我會(huì)分析上面代碼的作用,原理

  • executor:這是實(shí)例Promise對(duì)象時(shí)在構(gòu)造器中傳入的參數(shù),一般是一個(gè)function(resolve,reject){}
  • status:``Promise的狀態(tài),一開(kāi)始是默認(rèn)的pendding狀態(tài),每當(dāng)調(diào)用道resolve和reject方法時(shí),就會(huì)改變其值,在后面的then方法中會(huì)用到
  • value:resolve回調(diào)成功后,調(diào)用resolve方法里面的參數(shù)值
  • reason:reject回調(diào)成功后,調(diào)用reject方法里面的參數(shù)值
  • resolve:聲明resolve方法在構(gòu)造器內(nèi),通過(guò)傳入的executor方法傳入其中,用以給使用者回調(diào)
  • reject:聲明reject方法在構(gòu)造器內(nèi),通過(guò)傳入的executor方法傳入其中,用以給使用者回調(diào)

2.3 then

then方法是Promise中最為重要的方法,他的用法大家都應(yīng)該已經(jīng)知道,就是將Promise中的resolve或者reject的結(jié)果拿到,那么我們就能知道這里的then方法需要兩個(gè)參數(shù),成功回調(diào)和失敗回調(diào),上代碼!


            
  1. then(onFufilled,onRejected){
  2. if(this.status === 'resolve'){
  3. onFufilled(this.value)
  4. }
  5. if(this.status === 'reject'){
  6. onRejected(this.reason)
  7. }
  8. }

這里主要做了將構(gòu)造器中resolve和reject的結(jié)果傳入onFufilledonRejected中,注意這兩個(gè)是使用者傳入的參數(shù),是個(gè)方法。所以你以為這么簡(jiǎn)單就完了?要想更Swag的應(yīng)對(duì)各種場(chǎng)景,我們必須得再完善。繼續(xù)往下走!

3.異步的Promise

之前我們只是處理了同步情況下的Promise,簡(jiǎn)而言之所有操作都沒(méi)有異步的成分在內(nèi)。那么如果是異步該怎么辦?

3.1 callback?。。?!

最早處理異步的方法就是callback,就相當(dāng)于我讓你幫我掃地,我會(huì)在給你發(fā)起任務(wù)時(shí)給你一個(gè)手機(jī),之后我做自己的事情去,不用等你,等你掃完地就會(huì)打手機(jī)給我,誒,我就知道了地掃完了。這個(gè)手機(jī)就是callback,回調(diào)函數(shù)。

首先我們需要改一下構(gòu)造器里的代碼,分別添加兩個(gè)回調(diào)函數(shù)的數(shù)組,分別對(duì)應(yīng)成功回調(diào)和失敗回調(diào)。他們的作用是當(dāng)成功執(zhí)行resolve或reject時(shí),執(zhí)行callback。


            
  1. //存放成功回調(diào)的函數(shù)
  2. this.onResolvedCallbacks = []
  3. //存放失敗回調(diào)的函數(shù)
  4. this.onRejectedCallbacks = []
  5. let resolve = (data)=>{
  6. if(this.status==='pendding'){
  7. this.status = 'resolve'
  8. this.value = data
  9. //監(jiān)聽(tīng)回調(diào)函數(shù)
  10. this.onResolvedCallbacks.forEach(fn=>fn())
  11. }
  12. }
  13. let reject = (data)=>{
  14. if(this.status==='pendding'){
  15. this.status = 'reject'
  16. this.reason = data
  17. this.onRejectedCallbacks.forEach(fn=>fn())
  18. }
  19. }

然后是then需要多加一個(gè)狀態(tài)判斷,當(dāng)Promise中是異步操作時(shí),需要在我們之前定義的回調(diào)函數(shù)數(shù)組中添加一個(gè)回調(diào)函數(shù)。


            
  1. if(this.status === 'pendding'){
  2. this.onResolvedCallbacks.push(()=>{
  3. // to do....
  4. let x = onFufilled(this.value)
  5. resolvePromise(promise2,x,resolve,reject)
  6. })
  7. this.onRejectedCallbacks.push(()=>{
  8. let x = onRejected(this.reason)
  9. resolvePromise(promise2,x,resolve,reject)
  10. })
  11. }

ok!大功告成,異步已經(jīng)解決了

3.2 resolvePromise

這也是Promise中的重頭戲,我來(lái)介紹一下,我們?cè)谟肞romise的時(shí)候可能會(huì)發(fā)現(xiàn),當(dāng)then函數(shù)中return了一個(gè)值,我們可以繼續(xù)then下去,不過(guò)是什么值,都能在下一個(gè)then中獲取,還有,當(dāng)我們不在then中放入?yún)?shù),例:promise.then().then(),那么其后面的then依舊可以得到之前then返回的值,可能你現(xiàn)在想很迷惑。讓我來(lái)解開(kāi)你心中的憂愁,follow me。


            
  1. then(onFufilled,onRejected){
  2. //解決onFufilled,onRejected沒(méi)有傳值的問(wèn)題
  3. onFufilled = typeof onFufilled === 'function'?onFufilled:y=>y
  4. //因?yàn)殄e(cuò)誤的值要讓后面訪問(wèn)到,所以這里也要跑出個(gè)錯(cuò)誤,不然會(huì)在之后then的resolve中捕獲
  5. onRejected = typeof onRejected === 'function'?onRejected:err=>{ throw err ;}
  6. //聲明一個(gè)promise對(duì)象
  7. let promise2
  8. if(this.status === 'resolve'){
  9. //因?yàn)樵?then之后又是一個(gè)promise對(duì)象,所以這里肯定要返回一個(gè)promise對(duì)象
  10. promise2 = new Promise((resolve,reject)=>{
  11. setTimeout(()=>{
  12. //因?yàn)榇┩钢档木壒?,在默認(rèn)的跑出一個(gè)error后,不能再用下一個(gè)的reject來(lái)接受,只能通過(guò)try,catch
  13. try{
  14. //因?yàn)橛械臅r(shí)候需要判斷then中的方法是否返回一個(gè)promise對(duì)象,所以需要判斷
  15. //如果返回值為promise對(duì)象,則需要取出結(jié)果當(dāng)作promise2的resolve結(jié)果
  16. //如果不是,直接作為promise2的resolve結(jié)果
  17. let x = onFufilled(this.value)
  18. //抽離出一個(gè)公共方法來(lái)判斷他們是否為promise對(duì)象
  19. resolvePromise(promise2,x,resolve,reject)
  20. }catch(e){
  21. reject(e)
  22. }
  23. },0)
  24. })
  25. }
  26. if(this.status === 'reject'){
  27. promise2 = new Promise((resolve,reject)=>{
  28. setTimeout(()=>{
  29. try{
  30. let x = onRejected(this.reason)
  31. resolvePromise(promise2,x,resolve,reject)
  32. }catch(e){
  33. reject(e)
  34. }
  35. },0)
  36. })
  37. }
  38. if(this.status === 'pendding'){
  39. promise2 = new Promise((resolve,reject)=>{
  40. this.onResolvedCallbacks.push(()=>{
  41. // to do....
  42. setTimeout(()=>{
  43. try{
  44. let x = onFufilled(this.value)
  45. resolvePromise(promise2,x,resolve,reject)
  46. }catch(e){
  47. reject(e)
  48. }
  49. },0)
  50. })
  51. this.onRejectedCallbacks.push(()=>{
  52. setTimeout(()=>{
  53. try{
  54. let x = onRejected(this.reason)
  55. resolvePromise(promise2,x,resolve,reject)
  56. }catch(e){
  57. reject(e)
  58. }
  59. })
  60. })
  61. })
  62. }
  63. return promise2
  64. }

一下子多了很多方法,不用怕,我會(huì)一一解釋

  1. 返回Promise?:首先我們要注意的一點(diǎn)是,then有返回值,then了之后還能在then,那就說(shuō)明之前的then返回的必然是個(gè)Promise。
  2. 為什么外面要包一層setTimeout?:因?yàn)镻romise本身是一個(gè)異步方法,屬于微任務(wù)一列,必須得在執(zhí)行棧執(zhí)行完了在去取他的值,所以所有的返回值都得包一層異步setTimeout。
  3. 為什么開(kāi)頭有兩個(gè)判斷?:這就是之前想要解決的如果then函數(shù)中的參數(shù)不是函數(shù),那么我們需要做處理。如果onFufilled不是函數(shù),就需要自定義個(gè)函數(shù)用來(lái)返回之前resolve的值,如果onRejected不是函數(shù),自定義個(gè)函數(shù)拋出異常。這里會(huì)有個(gè)小坑,如果這里不拋出異常,會(huì)在下一個(gè)then的onFufilled中拿到值。又因?yàn)檫@里拋出了異常所以所有的onFufilled或者onRejected都需要try/catch,這也是Promise/A+的規(guī)范。當(dāng)然本人覺(jué)得成功的回調(diào)不需要拋出異常也可以,大家可以仔細(xì)想想。
  4. resolvePromise是什么?:這其實(shí)是官方Promise/A+的需求。因?yàn)槟愕膖hen可以返回任何職,當(dāng)然包括Promise對(duì)象,而如果是Promise對(duì)象,我們就需要將他拆解,直到它不是一個(gè)Promise對(duì)象,取其中的值。

那就讓我們來(lái)看看這個(gè)resolvePromise到底長(zhǎng)啥樣。


            
  1. function resolvePromise(promise2,x,resolve,reject){
  2. //判斷x和promise2之間的關(guān)系
  3. //因?yàn)閜romise2是上一個(gè)promise.then后的返回結(jié)果,所以如果相同,會(huì)導(dǎo)致下面的.then會(huì)是同一個(gè)promise2,一直都是,沒(méi)有盡頭
  4. if(x === promise2){//相當(dāng)于promise.then之后return了自己,因?yàn)閠hen會(huì)等待return后的promise,導(dǎo)致自己等待自己,一直處于等待
  5. return reject(new TypeError('循環(huán)引用'))
  6. }
  7. //如果x不是null,是對(duì)象或者方法
  8. if(x !== null && (typeof x === 'object' || typeof x === 'function')){
  9. //為了判斷resolve過(guò)的就不用再reject了,(比如有reject和resolve的時(shí)候)
  10. let called
  11. try{//防止then出現(xiàn)異常,Object.defineProperty
  12. let then = x.then//取x的then方法可能會(huì)取到{then:{}},并沒(méi)有執(zhí)行
  13. if(typeof then === 'function'){
  14. //我們就認(rèn)為他是promise,call他,因?yàn)閠hen方法中的this來(lái)自自己的promise對(duì)象
  15. then.call(x,y=>{//第一個(gè)參數(shù)是將x這個(gè)promise方法作為this指向,后兩個(gè)參數(shù)分別為成功失敗回調(diào)
  16. if(called) return;
  17. called = true
  18. //因?yàn)榭赡躳romise中還有promise,所以需要遞歸
  19. resolvePromise(promise2,y,resolve,reject)
  20. },err=>{
  21. if(called) return;
  22. called = true
  23. //一次錯(cuò)誤就直接返回
  24. reject(err)
  25. })
  26. }else{
  27. //如果是個(gè)普通對(duì)象就直接返回resolve作為結(jié)果
  28. resolve(x)
  29. }
  30. }catch(e){
  31. if(called) return;
  32. called = true
  33. reject(e)
  34. }
  35. }else{
  36. //這里返回的是非函數(shù),非對(duì)象的值,就直接放在promise2的resolve中作為結(jié)果
  37. resolve(x)
  38. }
  39. }

它的作用是用來(lái)將onFufilled的返回值進(jìn)行判斷取值處理,把最后獲得的值放入最外面那層的Promise的resolve函數(shù)中。

  1. 參數(shù)promise2(then函數(shù)返回的Promise對(duì)象),x(onFufilled函數(shù)的返回值),resolve、reject(最外層的Promise上的resolve和reject)。
  2. 為什么要在一開(kāi)始判斷promise2x?:首先在Promise/A+中寫(xiě)了需要判斷這兩者如果相等,需要拋出異常,我就來(lái)解釋一下為什么,如果這兩者相等,我們可以看下下面的例子,第一次p2是p1.then出來(lái)的結(jié)果是個(gè)Promise對(duì)象,這個(gè)Promise對(duì)象在被創(chuàng)建的時(shí)候調(diào)用了resolvePromise(promise2,x,resolve,reject)函數(shù),又因?yàn)閤等于其本身,是個(gè)Promise,就需要then方法遞歸它,直到他不是Promise對(duì)象,但是x(p2)的結(jié)果還在等待,他卻想執(zhí)行自己的then方法,就會(huì)導(dǎo)致等待。

            
  1. let p1 = new Promise((resolve,reject)=>{
  2. resolve()
  3. })
  4. let p2 = p1.then(d=>{
  5. return p2
  6. })
  1. called是用來(lái)干嘛的?:called變量主要是用來(lái)判斷如果resolvePromise函數(shù)已經(jīng)resolve或者reject了,那就不需要在執(zhí)行下面的resolce或者reject。
  2. 為什么取then這個(gè)屬性?:因?yàn)槲覀冃枰ヅ袛鄕是否為Promise,then屬性如果為普通值,就直接resolve掉,如果是個(gè)function,就是Promise對(duì)象,之后我們就需要將這個(gè)x的then方法進(jìn)行執(zhí)行,用call的原因是因?yàn)閠hen方法里面this指向的問(wèn)題。
  3. 為什么要遞歸去調(diào)用resolvePromise函數(shù)?:相信細(xì)心的人已經(jīng)發(fā)現(xiàn)了,我這里使用了遞歸調(diào)用法,首先這是Promise/A+中要求的,其次是業(yè)務(wù)場(chǎng)景的需求,當(dāng)我們碰到那種Promise的resolve里的Promise的resolve里又包了一個(gè)Promise的話,就需要遞歸取值,直到x不是Promise對(duì)象。

4.完善Promise

我們現(xiàn)在已經(jīng)基本完成了Promise的then方法,那么現(xiàn)在我們需要看看他的其他方法。

4.1 catch

相信大家都知道catch這個(gè)方法是用來(lái)捕獲Promise中的reject的值,也就是相當(dāng)于then方法中的onRejected回調(diào)函數(shù),那么問(wèn)題就解決了。我們來(lái)看代碼。


            
  1. //catch方法
  2. catch(onRejected){
  3. return this.then(null,onRejected)
  4. }

該方法是掛在Promise原型上的方法。當(dāng)我們調(diào)用catch傳callback的時(shí)候,就相當(dāng)于是調(diào)用了then方法。

4.2 resolve/reject

大家一定都看到過(guò)Promise.resolve()、Promise.reject()這兩種用法,它們的作用其實(shí)就是返回一個(gè)Promise對(duì)象,我們來(lái)實(shí)現(xiàn)一下。


            
  1. //resolve方法
  2. Promise.resolve = function(val){
  3. return new Promise((resolve,reject)=>{
  4. resolve(val)
  5. })
  6. }
  7. //reject方法
  8. Promise.reject = function(val){
  9. return new Promise((resolve,reject)=>{
  10. reject(val)
  11. })
  12. }

這兩個(gè)方法是直接可以通過(guò)class調(diào)用的,原理就是返回一個(gè)內(nèi)部是resolve或reject的Promise對(duì)象。

4.3 all

all方法可以說(shuō)是Promise中很常用的方法了,它的作用就是將一個(gè)數(shù)組的Promise對(duì)象放在其中,當(dāng)全部resolve的時(shí)候就會(huì)執(zhí)行then方法,當(dāng)有一個(gè)reject的時(shí)候就會(huì)執(zhí)行catch,并且他們的結(jié)果也是按著數(shù)組中的順序來(lái)排放的,那么我們來(lái)實(shí)現(xiàn)一下。


            
  1. //all方法(獲取所有的promise,都執(zhí)行then,把結(jié)果放到數(shù)組,一起返回)
  2. Promise.all = function(promises){
  3. let arr = []
  4. let i = 0
  5. function processData(index,data){
  6. arr[index] = data
  7. i++
  8. if(i == promises.length){
  9. resolve(arr)
  10. }
  11. }
  12. return new Promise((resolve,reject)=>{
  13. for(let i=0;i<promises.length;i++){
  14. promises[i].then(data=>{
  15. processData(i,data)
  16. },reject)
  17. }
  18. })
  19. }

其原理就是將參數(shù)中的數(shù)組取出遍歷,每當(dāng)執(zhí)行成功都會(huì)執(zhí)行processData方法,processData方法就是用來(lái)記錄每個(gè)Promise的值和它對(duì)應(yīng)的下標(biāo),當(dāng)執(zhí)行的次數(shù)等于數(shù)組長(zhǎng)度時(shí)就會(huì)執(zhí)行resolve,把a(bǔ)rr的值給then。這里會(huì)有一個(gè)坑,如果你是通過(guò)arr數(shù)組的長(zhǎng)度來(lái)判斷他是否應(yīng)該resolve的話就會(huì)出錯(cuò),為什么呢?因?yàn)閖s數(shù)組的特性,導(dǎo)致如果先出來(lái)的是1位置上的值進(jìn)arr,那么0位置上也會(huì)多一個(gè)空的值,所以不合理。

4.4 race

race方法雖然不常用,但是在Promise方法中也是一個(gè)能用得上的方法,它的作用是將一個(gè)Promise數(shù)組放入race中,哪個(gè)先執(zhí)行完,race就直接執(zhí)行完,并從then中取值。我們來(lái)實(shí)現(xiàn)一下吧。


            
  1. //race方法
  2. Promise.race = function(promises){
  3. return new Promise((resolve,reject)=>{
  4. for(let i=0;i<promises.length;i++){
  5. promises[i].then(resolve,reject)
  6. }
  7. })
  8. }

原理大家應(yīng)該看懂了,很簡(jiǎn)單,就是遍歷數(shù)組執(zhí)行Promise,如果有一個(gè)Promise執(zhí)行成功就resolve。

Promise語(yǔ)法糖 deferred

語(yǔ)法糖這三個(gè)字大家一定很熟悉,作為一個(gè)很Swag的前端工程師,對(duì)async/await這對(duì)兄弟肯定很熟悉,沒(méi)錯(cuò)他們就是generator的語(yǔ)法糖。而我們這里要講的語(yǔ)法糖是Promise的。


            
  1. //promise語(yǔ)法糖 也用來(lái)測(cè)試
  2. Promise.deferred = Promise.defer = function(){
  3. let dfd = {}
  4. dfd.promise = new Promise((resolve,reject)=>{
  5. dfd.resolve = resolve
  6. dfd.reject = reject
  7. })
  8. return dfd
  9. }

什么作用呢?看下面代碼你就知道了


            
  1. let fs = require('fs')
  2. let Promise = require('./promises')
  3. //Promise上的語(yǔ)法糖,為了防止嵌套,方便調(diào)用
  4. //壞處 錯(cuò)誤處理不方便
  5. function read(){
  6. let defer = Promise.defer()
  7. fs.readFile('./1.txt','utf8',(err,data)=>{
  8. if(err)defer.reject(err)
  9. defer.resolve(data)
  10. })
  11. return defer.Promise
  12. }

沒(méi)錯(cuò),我們可以方便的去調(diào)用他語(yǔ)法糖defer中的Promise對(duì)象。那么它還有沒(méi)有另外的方法呢?答案是有的。我們需要在全局上安裝promises-aplus-tests插件npm i promises-aplus-tests -g,再輸入promises-aplus-tests [js文件名] 即可驗(yàn)證你的Promise的規(guī)范。


藍(lán)藍(lán)設(shè)計(jì)sillybuy.com )是一家專注而深入的界面設(shè)計(jì)公司,為期望卓越的國(guó)內(nèi)外企業(yè)提供卓越的UI界面設(shè)計(jì)、BS界面設(shè)計(jì) 、 cs界面設(shè)計(jì) 、 ipad界面設(shè)計(jì) 、 包裝設(shè)計(jì) 、 圖標(biāo)定制 、 用戶體驗(yàn) 、交互設(shè)計(jì)、 網(wǎng)站建設(shè) 平面設(shè)計(jì)服務(wù)


分享本文至:

日歷

鏈接

個(gè)人資料

存檔