摘要:總結(jié)本文簡(jiǎn)要介紹了函數(shù)的一些特性,重點(diǎn)在于說明如何使用函數(shù)對(duì)異步任務(wù)進(jìn)行封裝,從而能夠讓異步代碼編寫的更加清晰。
在之前的文章介紹了傳統(tǒng)異步的實(shí)現(xiàn)方案,本文將介紹ES6中的一種全新的異步方案--Generator函數(shù)。
generator簡(jiǎn)介簡(jiǎn)單介紹一下generator的原理和語法,(更詳細(xì)內(nèi)容請(qǐng)看ECMAScript 6 入門,本文只介紹和異步相關(guān)的核心內(nèi)容)
基本語法通過一個(gè)簡(jiǎn)單的例子來了解generator函數(shù)
function* MyGenerator() { yield "yield的含義是:執(zhí)行此處時(shí),暫停執(zhí)行當(dāng)前函數(shù)" yield "暫停之后的函數(shù)可以用next方法繼續(xù)執(zhí)行" return "遇到return之后會(huì)真正結(jié)束,done會(huì)變成true" } const gen = MyGenerator() //執(zhí)行后返回的是一個(gè)指針,可以利用該指針執(zhí)行next方法 console.log(gen.next()) // {value: "yield的含義是:執(zhí)行此處時(shí),暫停執(zhí)行當(dāng)前函數(shù)“, done: false} console.log(gen.next())// {value: "暫停之后的函數(shù)可以用next方法繼續(xù)執(zhí)行", done: false} console.log(gen.next())// {value: "遇到return之后會(huì)真正結(jié)束,done會(huì)變成true", done: true}數(shù)據(jù)交互
數(shù)據(jù)交互指的是可以通過yield把當(dāng)前執(zhí)行的結(jié)果傳出,也可以使用next把外部參數(shù)傳入
function* MyGenerator(){ const result = yield "傳出的數(shù)據(jù)" return result } const gen = MyGenerator() console.log(gen.next())// generatorAndAsync2.html:19 {value: "傳出的數(shù)據(jù)", done: false} console.log(gen.next("傳入的數(shù)據(jù)"))// {value: "傳入的數(shù)據(jù)", done: true}
交互的參數(shù)類型當(dāng)然也可以是函數(shù):
function sayHi(){ console.log("hi"); } function* MyGenerator(){ const result = yield sayHi() return } const gen = MyGenerator() const result = gen.next()// {value: sayHi, done: false} result.value() // 正常輸出"hi"
具備以上最核心的兩個(gè)特性之后,generator就可以進(jìn)行異步操作封裝。
異步任務(wù)封裝首先,結(jié)合異步任務(wù)的特點(diǎn)以及前文提到的genrator函數(shù)的特性,提煉出使用generator封裝異步操作的核心思路:
在異步任務(wù)執(zhí)行時(shí),使用yield交出執(zhí)行權(quán)
在異步任務(wù)結(jié)束后,使用next交還執(zhí)行權(quán)
起步從一個(gè)最簡(jiǎn)單的例子開始:
// 1. 首先寫一個(gè)異步任務(wù),在一秒后返回特定字符串 function asyncTask(callback){ setTimeout(()=>{ callback("Hello Leo") }, 1000) } // 2. 接下來寫出期望執(zhí)行的順序 function* runTask() { let text = yield asyncTask console.log(text) // 我們期望這里正常輸出Hello Leo } // 3. 按照期望值執(zhí)行函數(shù) const gen = runTask()// 此時(shí)執(zhí)行權(quán)已經(jīng)交出 gen.next().value(function (text) {// 執(zhí)行asyncTask并傳入callback ,關(guān)鍵點(diǎn)在于在callback里調(diào)用next交還執(zhí)行權(quán) gen.next(text) })
首先,這段代碼雖然很粗糙,但是已經(jīng)反映了使用generator封裝異步任務(wù)的核心思想。最直觀的受益就是:runTask的內(nèi)容看起來很像同步代碼,條理清晰,很適合閱讀。
但是上面第3部分關(guān)于執(zhí)行的代碼很不靈活,我們不能每次都這么寫一段,因此接下來的目標(biāo)就是實(shí)現(xiàn)一個(gè)任務(wù)執(zhí)行器。
自動(dòng)任務(wù)執(zhí)行器同樣的,先思考核心的思路:要想讓某個(gè)generator函數(shù)自動(dòng)執(zhí)行,無非就是一個(gè)while循環(huán):
1. 如果當(dāng)前yield返回值的done屬性為true,說明任務(wù)已經(jīng)執(zhí)行完成; 2. 如果當(dāng)前yield返回值的done屬性為false,說明任務(wù)還未執(zhí)行完成,則繼續(xù)執(zhí)行next,同時(shí)要注意參數(shù)傳遞
根據(jù)分析實(shí)現(xiàn)以下的執(zhí)行器:
function autoExecute(task) { const gen = task() let result = gen.next() while(true){ if(result.done){ break // 執(zhí)行結(jié)束 return } console.log(result.value)//為了便于觀察 我們加上console.log result = gen.next(result.value) // 每次都應(yīng)該重寫result 獲取最新結(jié)果 } } function* simpleTask(){ yield 1 yield 2 yield 3 return } autoExecute(simpleTask)// 測(cè)試我們寫的自動(dòng)執(zhí)行器 能夠正確輸出123
上面的執(zhí)行器已經(jīng)有了雛形,但是對(duì)于前面例子中,result.value為函數(shù)的情況還沒有處理,因此還需要稍微補(bǔ)充:
function isFunction(source){ return Object.prototype.toString.call(source) === "[object Function]" } function autoExecute(task) { const gen = task() let result = gen.next() let isRuningAsync = false // 由于加入了異步處理,所以要增加一個(gè)標(biāo)志位避免重復(fù)進(jìn)入循環(huán)體 while (!isRuningAsync) { if (result.done) { return } console.log(result.value) /* start 補(bǔ)充的處理函數(shù) */ if (isFunction(result.value)) { isRuningAsync = true const callback = (arg) => { result = gen.next(arg) // 核心代碼 isRuningAsync = false } result.value(callback) /* end 補(bǔ)充的處理函數(shù) */ } else { result = gen.next(result.value) } } } autoExecute(runTask) // 試著用這個(gè)自動(dòng)執(zhí)行器執(zhí)行之前的異步任務(wù)
上面這個(gè)自動(dòng)執(zhí)行器,就完成了generator對(duì)異步任務(wù)的封裝。
總結(jié)本文簡(jiǎn)要介紹了generator函數(shù)的一些特性,重點(diǎn)在于說明如何使用generator函數(shù)對(duì)異步任務(wù)進(jìn)行封裝,從而能夠讓異步代碼編寫的更加清晰。
再次強(qiáng)調(diào):用generator函數(shù)對(duì)異步任務(wù)封裝的思想是很明確的--控制 Generator 函數(shù)的流程,在適當(dāng)?shù)臅r(shí)機(jī)接收和交還程序的執(zhí)行權(quán),但是具體的實(shí)現(xiàn)方式并不唯一,例如本文用的是最簡(jiǎn)單直接的回調(diào)函數(shù)方式,在阮一峰老師的《es6入門》教程里,也有使用thunk思路來講解的部分,可以自行查閱。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/109850.html
摘要:前文該系列下的前幾篇文章分別對(duì)不同的幾種異步方案原理進(jìn)行解析,本文將介紹一些實(shí)際場(chǎng)景和一些常見的面試題。流程調(diào)度里比較常見的一種錯(cuò)誤是看似串行的寫法,可以感受一下這個(gè)例子判斷以下幾種寫法的輸出結(jié)果辨別輸出順序這類題目一般出現(xiàn)在面試題里。 前文 該系列下的前幾篇文章分別對(duì)不同的幾種異步方案原理進(jìn)行解析,本文將介紹一些實(shí)際場(chǎng)景和一些常見的面試題。(積累不太夠,后面想到再補(bǔ)) 正文 流程調(diào)度...
摘要:在誕生以前,異步編程的方式大概有下面四種回調(diào)函數(shù)事件監(jiān)聽發(fā)布訂閱對(duì)象將異步編程帶入了一個(gè)全新的階段,中的函數(shù)更是給出了異步編程的終極解決方案。這意味著,出錯(cuò)的代碼與處理錯(cuò)誤的代碼,實(shí)現(xiàn)了時(shí)間和空間上的分離,這對(duì)于異步編程無疑是很重要的。 寫在前面 有一個(gè)有趣的問題: 為什么Node.js約定回調(diào)函數(shù)的第一個(gè)參數(shù)必須是錯(cuò)誤對(duì)象err(如果沒有錯(cuò)誤,該參數(shù)就是null)? 原因是執(zhí)行回調(diào)函...
摘要:回調(diào)函數(shù)這是異步編程最基本的方法。對(duì)象對(duì)象是工作組提出的一種規(guī)范,目的是為異步編程提供統(tǒng)一接口。誕生后,出現(xiàn)了函數(shù),它將異步編程帶入了一個(gè)全新的階段。 更多詳情點(diǎn)擊http://blog.zhangbing.club/Ja... Javascript 語言的執(zhí)行環(huán)境是單線程的,如果沒有異步編程,根本沒法用,非卡死不可。 為了解決這個(gè)問題,Javascript語言將任務(wù)的執(zhí)行模式分成兩種...
摘要:接下來我們看下三類異步編程的實(shí)現(xiàn)。事件監(jiān)聽事件發(fā)布訂閱事件監(jiān)聽是一種非常常見的異步編程模式,它是一種典型的邏輯分離方式,對(duì)代碼解耦很有用處。 一、 一道面試題 前段時(shí)間面試,考察比較多的是js異步編程方面的相關(guān)知識(shí)點(diǎn),如今,正好輪到自己分享技術(shù),所以想把js異步編程學(xué)習(xí)下,做個(gè)總結(jié)。下面這個(gè)demo 概括了大多數(shù)面試過程中遇到的問題: for(var i = 0; i < 3; i++...
摘要:回調(diào)函數(shù)這是最原始的一種異步解決方法。從的對(duì)象演化而來對(duì)象是提出的一種對(duì)異步編程的解決方案,但它不是新的語法,而是一種新的寫法,允許將回調(diào)函數(shù)的嵌套改成鏈?zhǔn)秸{(diào)用。 一、前言 異步編程對(duì)JavaScript來說非常重要,因?yàn)镴avaScript的語言環(huán)境是單線程的,如果沒有異步編程將變得非常可怕,估計(jì)根本無法使用。這篇文章就來總結(jié)一下從最原始的回調(diào)函數(shù)到現(xiàn)在的ES6、ES7的新方法。 文...
閱讀 773·2023-04-25 19:53
閱讀 4413·2021-09-22 15:13
閱讀 2635·2019-08-30 10:56
閱讀 1383·2019-08-29 16:27
閱讀 3020·2019-08-29 14:00
閱讀 2492·2019-08-26 13:56
閱讀 621·2019-08-26 13:29
閱讀 1684·2019-08-26 11:31