Generator
+ Promise
實現異步編程結合 上一篇文章 ,咱們來聊聊 Generator
javascript
說到異步編程,你想到的是async
和 await
,但那也只是 Generator
的語法糖而已。dva 中有一個 Effect
的概念,它就是使用 Generator
來解決異步請求的問題,咱們也來聊一聊 Generator
+ Promise
如何異步編程:html
開始以前,咱們須要瞭解一些基本的概念:前端
Generator
做爲 ES6
中使用協程的解決方案來處理異步編程的具體實現,它的特色是: Generator
中可使用 yield
關鍵字配合實例 gen
調用 next()
方法,來將其內部的語句分割執行。 簡言之 : next()
被調用一次,則 yield
語句被執行一句,隨着 next()
調用, yield
語句被依次執行。java
Promise
表示一個異步操做的最終狀態(完成或失敗),以及其返回的值。參考Promise-MDNgit
因此,異步編程使用 Generator
和 Promise
來實現的原理是什麼呢?github
Generator
自己 yield
語句是分離執行的,因此咱們利用這一點,在 yield
語句中返回一個 Promise
對象Generator
中的 next()
後, 假設返回值叫 result
,那麼此時 result.value
就是咱們定義在 yield
語句中的 Promise
對象注意:在這一步,咱們已經把原來的執行流程暫停,轉而執行 Promise
的內容,已經實現了控制異步代碼的執行,由於此時咱們若是不繼續執行 next()
則 generator
中位於當前被執行的 yield
後面的內容,將不會繼續執行,這已經達到了咱們須要的效果編程
接下來咱們就是在執行完當前 Promise
以後,讓代碼繼續往下執行,直到遇到下一個 yield
語句:
這一步是最關鍵的 因此咱們怎麼作呢:promise
步驟1: 在當前的 Promise
的 then()
方法中,繼續執行 gen.next()
app
步驟2: 當 gen.next()
返回的結果 result.done === true
時,咱們拿到 result.value
【也就是一個新的 Promise
對象】再次執行而且在它的then()
方法中繼續上面的步驟1,直至 result.done === false
的時候。這時候調用 resolve()
使 promise
狀態改變,由於全部的 yield
語句已經被執行完。異步
yield
語句yield
語句執行完不會中斷,直至 Generator
中的最後一個 yield
語句被執行完。 流程示意圖: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庫的源碼。
至此實現異步操做的控制。
小冊 你不知道的 Chrome 調試技巧 已經開始預售啦。
歡迎關注公衆號 「前端惡霸」,掃碼關注,會有不少好東西等着你~