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