Generator
+ Promise
實現異步編程結合 上一篇文章 ,咱們來聊聊 Generator
javascript
說到異步編程,你想到的是async
和 await
,但那也只是 Generator
的語法糖而已。dva 中有一個 Effect
的概念,它就是使用 Generator
來解決異步請求的問題,咱們也來聊一聊 Generator
+ Promise
如何異步編程:html
開始以前,咱們須要瞭解一些基本的概念:前端
Generator
做爲 ES6
中使用協程的解決方案來處理異步編程的具體實現,它的特色是: Generator
中可使用 yield
關鍵字配合實例 gen
調用 next()
方法,來將其內部的語句分割執行。 簡言之 : next()
被調用一次,則 yield
語句被執行一句,隨着 next()
調用, yield
語句被依次執行。Promise
表示一個異步操做的最終狀態(完成或失敗),以及其返回的值。參考Promise-MDN 因此,異步編程使用 Generator
和 Promise
來實現的原理是什麼呢?java
Generator
自己 yield
語句是分離執行的,因此咱們利用這一點,在 yield
語句中返回一個 Promise
對象Generator
中的 next()
後, 假設返回值叫 result
,那麼此時 result.value
就是咱們定義在 yield
語句中的 Promise
對象注意:在這一步,咱們已經把原來的執行流程暫停,轉而執行 Promise
的內容,已經實現了控制異步代碼的執行,由於此時咱們若是不繼續執行 next()
則 generator
中位於當前被執行的 yield
後面的內容,將不會繼續執行,這已經達到了咱們須要的效果git
接下來咱們就是在執行完當前 Promise
以後,讓代碼繼續往下執行,直到遇到下一個 yield
語句:
這一步是最關鍵的 因此咱們怎麼作呢:github
步驟1: 在當前的 Promise
的 then()
方法中,繼續執行 gen.next()
編程
步驟2: 當 gen.next()
返回的結果 result.done === true
時,咱們拿到 result.value
【也就是一個新的 Promise
對象】再次執行而且在它的then()
方法中繼續上面的步驟1,直至 result.done === false
的時候。這時候調用 resolve()
使 promise
狀態改變,由於全部的 yield
語句已經被執行完。promise
yield
語句yield
語句執行完不會中斷,直至 Generator
中的最後一個 yield
語句被執行完。流程示意圖:app
co 是著名大神 TJ 實現的Generator
的二次封裝庫,那麼咱們就從co
庫中的一個demo開始,瞭解咱們的整個異步請求封裝實現:
co(function*() { yield me.loginAction(me.form); ... });
在這裏咱們引入了co
庫,而且用co
來包裹了一個generator
(生成器)對象。
接下來咱們看下co
對於包裹起來的generator
作了什麼處理異步
function co(gen) { // 1.獲取當前co函數的執行上下文環境,獲取到參數列表 var ctx = this; var args = slice.call(arguments, 1); // 2.返回一個Promise對象 return new Promise(function(resolve, reject) { // 判斷而且使用ctx:context(上下文環境)和arg:arguments(參數列表)初始化generator而且複製給gen // 注意: // gen = gen.apply(ctx, args)以後 // 咱們調用 gen.next() 時,返回的是一個指針,實際的值是一個對象 // 對象的形式:{done:[false | true], value: ''} if (typeof gen === 'function') gen = gen.apply(ctx, args); // 當返回值不爲gen時或者gen.next的類型不爲function【實際是判斷是否爲generator】時 // 當前promise狀態被設置爲resolve而結束 if (!gen || typeof gen.next !== 'function') return resolve(gen); // 不然執行onFulfilled() onFulfilled(); }); }
總結一下這裏發生了什麼
promise
promise
中將被包裹的 generator
實例化爲一個指針,指向 generator
中第一個 yield
語句generator
實例化出來的指針是否存在:若是沒有 yield
語句則指針不存在gen.next()
方法是否爲 function
:若是不爲 function
證實沒法執行 gen.next()
promise
的狀態置爲 resolve
onFulfilled()
接下來咱們看下 onFulfilled()
的實現
function onFulfilled(res) { // 在執行onFulfilled時,定義了一個ret來儲存gen.next(res)執行後的指針對象 var ret; try { ret = gen.next(res); // 在這裏,yield語句拋出的值就是{value:me.loginAction(me.form), done:false} } catch (e) { return reject(e); } // 將ret對象傳入到咱們定義在promise中的next方法中 next(ret); return null; }
總結一下,onFulfilled
最主要的工做就是
gen.next()
使代碼執行到 yield
語句next()
方法中那麼咱們再來看 next()
方法
function next(ret) { // 進入next中首先判斷咱們傳入的ret的done狀態: // 狀況1:ret.done = true 表明咱們這個generator中全部yield語句都已經執行完。 // 那麼將ret.value傳入到resolve()中,promise的狀態變成解決,整個過程結束。 if (ret.done) return resolve(ret.value); // 狀況2:當前ret.done = false 表明generator還未將全部的yield語句執行完,那麼這時候 // 咱們把當前上下文和ret.value傳入toPromise中,將其轉換爲對應的Promise對象`value` var value = toPromise.call(ctx, ret.value); if (value && isPromise(value)) return value.then(onFulfilled, onRejected); // 當value確實是一個promise對象的時候,return value.then(onFulfilled,onRejected) // 咱們從新進入到了generator中,執行下一條yield語句 return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, ' + 'but the following object was passed: "' + String(ret.value) + '"')); }
總結一下,next
主要工做
yield
語句的執行結果yield
的 result
的 value
值【其實就是咱們要異步執行的 Promise
】value
的 then
方法,從新進入到 onFulfilled
方法中,而在 onFulfilled
中,咱們又將進入到當前方法,如此循環的調用,實現了 generator
和 Promise
的執行切換,從而實現了 Promise
的內容按照咱們所定義的順序執行。有同窗可能對這裏的 toPromise
方法有一些疑惑,我先把代碼貼出來
function toPromise(obj) { if (!obj) return obj; if (isPromise(obj)) return obj; if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj); if ('function' == typeof obj) return thunkToPromise.call(this, obj); if (Array.isArray(obj)) return arrayToPromise.call(this, obj); if (isObject(obj)) return objectToPromise.call(this, obj); return obj; }
其實這個函數作的事情就是,根據不一樣的類型進行轉換,使得最後輸出的類型都是一個 Promise
。那具體的轉換細節,你們能夠參考co庫的源碼。
至此實現異步操做的控制。
這裏是 Dendoink ,奇舞週刊原創做者,掘金 [聯合編輯 / 小冊做者] 。
對於技術人而言:技 是單兵做戰能力,術 則是運用能力的方法。駕輕就熟,出神入化就是 藝 。在前端娛樂圈,我想成爲一名出色的人民藝術家。掃碼關注公衆號 前端惡霸 我在這裏等你: