promise和async的實(shí)現(xiàn)

promise的含義

promise是一個(gè)異步編程的解決方法,簡單來說,promise類似一個(gè)容器,里面保存著未來某個(gè)時(shí)間點(diǎn)才會(huì)結(jié)束的事件。

   new Promise((resolve,reject)=>{
       if(成功)){
           resolve()
       } else {
           reject()
       }
   })

而捕抓錯(cuò)誤的寫法是:

   promise.then(function(data){
       // success
   })
   .catch(function(err){
       // err
   })

promise有三種狀態(tài)(并且狀態(tài)是不可逆的):

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

狀態(tài)改變,只能從padding變成fulfilled或者rejected


async函數(shù)的實(shí)現(xiàn)原理

async 函數(shù)返回一個(gè)promise對象,當(dāng)函數(shù)執(zhí)行的時(shí)候,一旦遇到await就會(huì)先返回,等到觸發(fā)的異步操作完成后再繼續(xù)執(zhí)行后面的語句

image

async / await 的使用,只需要在function外加速async 然后再函數(shù)內(nèi)部異步執(zhí)行的函數(shù)添加await

Generator 函數(shù)

Generator函數(shù)是es6提供的一種異步編程的解決方案,語法行為與傳統(tǒng)函數(shù)完全不一樣。Generator函數(shù)最大的特點(diǎn)就是交出函數(shù)的執(zhí)行權(quán),并且可以多次返回,每次返回值作為迭代器的一部分保存下來,可以被我顯示調(diào)用

從語法上,我們可以把他想象成一個(gè)狀態(tài)機(jī),封裝了很多個(gè)內(nèi)部狀態(tài)。

執(zhí)行Generator函數(shù)會(huì)返回一個(gè)遍歷對象,也就是Generator函數(shù)除了是狀態(tài)機(jī)還是一個(gè)遍歷對象生成函數(shù)。返回遍歷對象,可以依次遍歷Generator函數(shù)內(nèi)部的每一個(gè)狀態(tài)。

形式上Generator函數(shù)是一個(gè)普通函數(shù),但有兩個(gè)特征。

function關(guān)鍵字與函數(shù)名之間有個(gè)*
函數(shù)體內(nèi)使用yield表達(dá)式,定義不同的內(nèi)部狀態(tài)

而且當(dāng)你直接調(diào)用一個(gè)Generator函數(shù),它不會(huì)直接執(zhí)行,返回的也不是函數(shù)運(yùn)行結(jié)果,而是指向內(nèi)部應(yīng)用狀態(tài)的指針對象,就是遍歷對象

然后下一步,必須調(diào)用遍歷器對象的next方法,使得指針移向下一個(gè)狀態(tài)。也就是說,每次調(diào)用next方法,內(nèi)部指針就從函數(shù)頭部,或者上次的地方執(zhí)行,直到遇到下一個(gè)yield表達(dá)式(或者return語句為止)

所以你可以把yield當(dāng)成一個(gè)暫停標(biāo)記,而next方法可以恢復(fù)執(zhí)行

而訪問每一個(gè)狀態(tài)就需要調(diào)用next。每次next會(huì)自動(dòng)執(zhí)行到下一個(gè)yield上,直到return

   
function* gen(){
   yield "hello";
   yield "world";
   return "ends"
}


let g1=gen()                // gen{<suspended>}

console.log(g1.next());    //  {value:'hello',done:false}

console.log(g1.next())     //  {value:"world",done:false}

console.log(g1.next())     //  {value:"ends",done:true}

console.log(g1.next())     // {value:undefined,done:true}

console.log(g1.next())     // {value:undefined,done:true}

Generator函數(shù)的實(shí)例。它具有狀態(tài)值suspended和closed,suspended代表暫停,closed則為結(jié)束

一旦執(zhí)行過return之后,,以后再調(diào)用這個(gè)方法返回的也是這個(gè)值

總結(jié):

調(diào)用Generator函數(shù),返回的都是一個(gè)遍歷對象,代表Generator函數(shù)內(nèi)部的指針,之后的每次調(diào)Generator的next方法,就會(huì)返回一個(gè)有著valuedone兩個(gè)屬性。value屬性表示當(dāng)前內(nèi)部的值,也就是yield的后面的值。 done屬性的值,是一個(gè)布爾值,表示遍歷是否已經(jīng)結(jié)束。


yield
yield表達(dá)式

由于Generator函數(shù)返回的是遍歷對象,而要想繼續(xù)執(zhí)行則需要調(diào)用next方法才能執(zhí)行下一個(gè)內(nèi)部的狀態(tài),所以實(shí)際提供了一個(gè)可以暫停的函數(shù)函數(shù)就是yield

關(guān)于next的運(yùn)行邏輯如下:

  1. 遇到y(tǒng)ield表達(dá)式,就暫停執(zhí)行后面的操作,并且將yield后面的值作為對象的value
  2. 下一次調(diào)用next方法的時(shí)候,再繼續(xù)往下執(zhí)行,直到遇到下一個(gè)yield表達(dá)式
  3. 如果沒有遇到下一個(gè)yield,則會(huì)直接運(yùn)行到函數(shù)結(jié)束,直到return為止,并且返回return的值
  4. 如果沒有return,則返回的value中為undefined

yield的存在,相當(dāng)于為javascript提供了惰性求值的語法功能。

如果Generator函數(shù)內(nèi)部不使用yieldb表達(dá)式,這時(shí)就變成了一個(gè)單純的暫緩執(zhí)行函數(shù)

       function* gen(){
       console.log("執(zhí)行了!")
   }

   let g1=gen()

   console.log(g1);   //  gen {<suspended>}

像上述代碼,如果你想執(zhí)行,則需要使用next函數(shù)才能執(zhí)行

   function* gen(){
       console.log("執(zhí)行了!");  //  執(zhí)行了!
   }

   let g1=gen()

   console.log(g1.next());   //  {value:undefined,done:true}

而且yield只能只用在Generator函數(shù)中,使用在普通函數(shù)中會(huì)報(bào)錯(cuò)。

另外,yield表達(dá)式如果在另一個(gè)表達(dá)式中,必須放上圓括號(hào)

  function* demo() {
  console.log('Hello' + yield); // SyntaxError
  console.log('Hello' + yield 123); // SyntaxError

  console.log('Hello' + (yield)); // OK
  console.log('Hello' + (yield 123)); // OK
}

yield 委托*

在Generator函數(shù)中有時(shí)候,會(huì)需要將多個(gè)迭代器的值合在一起。我們可以用yield*形式,將執(zhí)行委托給另外一個(gè)Generator函數(shù)

  function* foo1() {
  yield 1;
  yield 2;
  return "foo1 end";
}

function* foo2() {
  yield 3;
  yield 4;
}

function* foo() {
  yield* foo1();
  yield* foo2();
  yield 5;
}

const iterator = foo();

console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: 4, done: false }"
console.log(iterator.next()); // "{ value: 5, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

我們可以看到,并沒有執(zhí)行foo1中的return語句,那是因?yàn)?,在整個(gè)Generator函數(shù)只能有一次return,所有的yield*在Generator函數(shù)都是以函數(shù)表達(dá)式的形式出現(xiàn)的。return的值是表達(dá)式的結(jié)果,在委托結(jié)束之前其內(nèi)部都是停止的,等待到表達(dá)式的結(jié)束的時(shí)候,直接返回給foo,但是此時(shí)foo沒有接收這個(gè)變量所以,并未打印

如圖就是接收foo1中return值的寫法

image

next方法傳參

yield本身沒有返回值,它總是會(huì)返回undefined。next方法可以攜帶一個(gè)參數(shù),該參數(shù)會(huì)被當(dāng)作yield表達(dá)式的值
如下圖:


image

同理如果next不攜帶參數(shù),它返回的就是undefined

由于Generator函數(shù)從暫停狀態(tài)到恢復(fù)運(yùn)行,它的上下文(context)是不變的,通過next傳參這個(gè)功能可以讓Generator函數(shù)在不同階段的運(yùn)行階段,從外部向內(nèi)部注入值,從而調(diào)整函數(shù)的行為

throw方法

throw根據(jù)函數(shù)中書寫try catch返回catch中的內(nèi)容,如果沒有寫try則直接拋出異常


image

可以看到一旦我們拋出了錯(cuò)誤,那么Generator的狀態(tài)直接就會(huì)改變成true。而且value會(huì)變成undefined。

需要注意的是,如果我們執(zhí)行throw()的時(shí)候Generator函數(shù)里面沒有try Catch則會(huì)直接報(bào)錯(cuò)。如下

function* foo(x) {
   yield x + 1
   try {
       yield x + 2
   } catch (e) {
       console.log('catch it')
   }
}
const result = foo(0) // foo {<suspended>}
result.next(); // {value: 1, done: false}
result.throw(); // Uncaught undefined
result.next(); //{value: undefined, done: true}

上面的錯(cuò)誤是因?yàn)?,此時(shí)的語句還沒執(zhí)行到try里面。


iterator 迭代器

任意一個(gè)對象的Symbol.iterator方法,等于該對象的遍歷器生成函數(shù),調(diào)用該函數(shù)會(huì)返回對象的一個(gè)遍歷器對象

而由于Generator函數(shù)就是遍歷器生成函數(shù),因此可以把Generator賦值給對象的Symbol.iterator屬性,從而使得該對象具有Iterator接口

image

可以看到嘗試使用for...of遍歷object時(shí),會(huì)報(bào)錯(cuò)obj is not iterable對象不能被遍歷

下面我們嘗試使用iterator接口

image

如果對象是key:value類型,控制臺(tái)也能打印出相對應(yīng)的value

image
實(shí)現(xiàn) Async await
image

在執(zhí)行過程中,判斷一個(gè)函數(shù)的promise是否已經(jīng)執(zhí)行完(因?yàn)閜romise完成會(huì)return值,return代表這個(gè)yield表達(dá)式以及完成),如果完成直接返回值,沒有則繼續(xù)重復(fù)這個(gè)步驟

總結(jié)

通過理解了Generator函數(shù),再看async/await的實(shí)現(xiàn)是比較簡單的。因?yàn)槠鋵?shí)就是利用Generator函數(shù)的能夠暫停的特性,和通過yield能夠獲取表達(dá)式的執(zhí)行狀態(tài),從而去判斷promise的執(zhí)行是否已經(jīng)完成。


參考資料:http://www.itdecent.cn/p/f703d3f54ebd
參考資料:http://www.itdecent.cn/p/0f1b6ae1888c
參考資料: https://mp.weixin.qq.com/s/gT9MF1l52HuKUpmeNIByCQ

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容