摘要:你能學(xué)到什么如何使用實(shí)現(xiàn)異步編程異步編程的原理解析前言結(jié)合上一篇文章,我們來(lái)聊聊基礎(chǔ)原理說(shuō)到異步編程,你想到的是和,但那也只是的語(yǔ)法糖而已。表示一個(gè)異步操作的最終狀態(tài)完成或失敗,以及其返回的值。至此實(shí)現(xiàn)異步操作的控制。
你能學(xué)到什么
如何使用 Generator + Promise 實(shí)現(xiàn)異步編程
異步編程的原理解析
前言結(jié)合 上一篇文章 ,我們來(lái)聊聊 Generator
基礎(chǔ)原理說(shuō)到異步編程,你想到的是async 和 await ,但那也只是 Generator 的語(yǔ)法糖而已。dva 中有一個(gè) Effect 的概念,它就是使用 Generator 來(lái)解決異步請(qǐng)求的問(wèn)題,我們也來(lái)聊一聊 Generator + Promise 如何異步編程:
開始之前,我們需要了解一些基本的概念:
Generator作為 ES6 中使用協(xié)程的解決方案來(lái)處理異步編程的具體實(shí)現(xiàn),它的特點(diǎn)是: Generator 中可以使用 yield 關(guān)鍵字配合實(shí)例 gen 調(diào)用 next() 方法,來(lái)將其內(nèi)部的語(yǔ)句分割執(zhí)行。 簡(jiǎn)言之 : next() 被調(diào)用一次,則 yield 語(yǔ)句被執(zhí)行一句,隨著 next() 調(diào)用, yield 語(yǔ)句被依次執(zhí)行。
Promise表示一個(gè)異步操作的最終狀態(tài)(完成或失敗),以及其返回的值。參考Promise-MDN
所以,異步編程使用 Generator 和 Promise 來(lái)實(shí)現(xiàn)的原理是什么呢?
因?yàn)?Generator 本身 yield 語(yǔ)句是分離執(zhí)行的,所以我們利用這一點(diǎn),在 yield 語(yǔ)句中返回一個(gè) Promise 對(duì)象
首次調(diào)用 Generator 中的 next() 后, 假設(shè)返回值叫 result ,那么此時(shí) result.value 就是我們定義在 yield 語(yǔ)句中的 Promise 對(duì)象
注意:在這一步,我們已經(jīng)把原來(lái)的執(zhí)行流程暫停,轉(zhuǎn)而執(zhí)行 Promise 的內(nèi)容,已經(jīng)實(shí)現(xiàn)了控制異步代碼的執(zhí)行,因?yàn)榇藭r(shí)我們?nèi)绻焕^續(xù)執(zhí)行 next() 則 generator 中位于當(dāng)前被執(zhí)行的 yield 后面的內(nèi)容,將不會(huì)繼續(xù)執(zhí)行,這已經(jīng)達(dá)到了我們需要的效果
接下來(lái)我們就是在執(zhí)行完當(dāng)前 Promise 之后,讓代碼繼續(xù)往下執(zhí)行,直到遇到下一個(gè) yield 語(yǔ)句:
這一步是最關(guān)鍵的 所以我們?cè)趺醋瞿?
步驟1: 在當(dāng)前的 Promise 的 then() 方法中,繼續(xù)執(zhí)行 gen.next()
步驟2: 當(dāng) gen.next() 返回的結(jié)果 result.done === true 時(shí),我們拿到 result.value【也就是一個(gè)新的 Promise 對(duì)象】再次執(zhí)行并且在它的then() 方法中繼續(xù)上面的步驟1,直至 result.done === false 的時(shí)候。這時(shí)候調(diào)用 resolve() 使 promise 狀態(tài)改變,因?yàn)樗械?yield 語(yǔ)句已經(jīng)被執(zhí)行完。
步驟1 保證了我們可以走到下一個(gè) yield 語(yǔ)句
步驟2 保證了下一個(gè) yield 語(yǔ)句執(zhí)行完不會(huì)中斷,直至 Generator 中的最后一個(gè) yield 語(yǔ)句被執(zhí)行完。
流程示意圖:
具體實(shí)現(xiàn)co 是著名大神 TJ 實(shí)現(xiàn)的 Generator 的二次封裝庫(kù),那么我們就從co庫(kù)中的一個(gè)demo開始,了解我們的整個(gè)異步請(qǐng)求封裝實(shí)現(xiàn):
co(function*() { yield me.loginAction(me.form); ... });
在這里我們引入了co庫(kù),并且用co來(lái)包裹了一個(gè)generator(生成器)對(duì)象。
接下來(lái)我們看下co對(duì)于包裹起來(lái)的generator做了什么處理
function co(gen) { // 1.獲取當(dāng)前co函數(shù)的執(zhí)行上下文環(huán)境,獲取到參數(shù)列表 var ctx = this; var args = slice.call(arguments, 1); // 2.返回一個(gè)Promise對(duì)象 return new Promise(function(resolve, reject) { // 判斷并且使用ctx:context(上下文環(huán)境)和arg:arguments(參數(shù)列表)初始化generator并且復(fù)制給gen // 注意: // gen = gen.apply(ctx, args)之后 // 我們調(diào)用 gen.next() 時(shí),返回的是一個(gè)指針,實(shí)際的值是一個(gè)對(duì)象 // 對(duì)象的形式:{done:[false | true], value: ""} if (typeof gen === "function") gen = gen.apply(ctx, args); // 當(dāng)返回值不為gen時(shí)或者gen.next的類型不為function【實(shí)際是判斷是否為generator】時(shí) // 當(dāng)前promise狀態(tài)被設(shè)置為resolve而結(jié)束 if (!gen || typeof gen.next !== "function") return resolve(gen); // 否則執(zhí)行onFulfilled() onFulfilled(); }); }
總結(jié)一下這里發(fā)生了什么
返回一個(gè) promise
promise 中將被包裹的 generator 實(shí)例化為一個(gè)指針,指向 generator 中第一個(gè) yield 語(yǔ)句
判斷 generator 實(shí)例化出來(lái)的指針是否存在:如果沒(méi)有 yield 語(yǔ)句則指針不存在
判斷指針 gen.next() 方法是否為 function :如果不為 function 證明無(wú)法執(zhí)行 gen.next()
條件有一項(xiàng)不滿足就將 promise 的狀態(tài)置為 resolve
否則執(zhí)行 onFulfilled()
接下來(lái)我們看下 onFulfilled() 的實(shí)現(xiàn)
function onFulfilled(res) { // 在執(zhí)行onFulfilled時(shí),定義了一個(gè)ret來(lái)儲(chǔ)存gen.next(res)執(zhí)行后的指針對(duì)象 var ret; try { ret = gen.next(res); // 在這里,yield語(yǔ)句拋出的值就是{value:me.loginAction(me.form), done:false} } catch (e) { return reject(e); } // 將ret對(duì)象傳入到我們定義在promise中的next方法中 next(ret); return null; }
總結(jié)一下,onFulfilled 最主要的工作就是
執(zhí)行 gen.next() 使代碼執(zhí)行到 yield 語(yǔ)句
將執(zhí)行后返回的結(jié)果傳入我們自定義的 next() 方法中
那么我們?cè)賮?lái)看 next() 方法
function next(ret) { // 進(jìn)入next中首先判斷我們傳入的ret的done狀態(tài): // 情況1:ret.done = true 代表我們這個(gè)generator中所有yield語(yǔ)句都已經(jīng)執(zhí)行完。 // 那么將ret.value傳入到resolve()中,promise的狀態(tài)變成解決,整個(gè)過(guò)程結(jié)束。 if (ret.done) return resolve(ret.value); // 情況2:當(dāng)前ret.done = false 代表generator還未將所有的yield語(yǔ)句執(zhí)行完,那么這時(shí)候 // 我們把當(dāng)前上下文和ret.value傳入toPromise中,將其轉(zhuǎn)換為對(duì)應(yīng)的Promise對(duì)象`value` var value = toPromise.call(ctx, ret.value); if (value && isPromise(value)) return value.then(onFulfilled, onRejected); // 當(dāng)value確實(shí)是一個(gè)promise對(duì)象的時(shí)候,return value.then(onFulfilled,onRejected) // 我們重新進(jìn)入到了generator中,執(zhí)行下一條yield語(yǔ)句 return onRejected(new TypeError("You may only yield a function, promise, generator, array, or object, " + "but the following object was passed: "" + String(ret.value) + """)); }
總結(jié)一下,next 主要工作
判斷上一次 yield 語(yǔ)句的執(zhí)行結(jié)果
將 yield 的 result 的 value 值【其實(shí)就是我們要異步執(zhí)行的 Promise 】
執(zhí)行 value 的 then 方法,重新進(jìn)入到 onFulfilled 方法中,而在 onFulfilled 中,我們又將進(jìn)入到當(dāng)前方法,如此循環(huán)的調(diào)用,實(shí)現(xiàn)了 generator 和 Promise 的執(zhí)行切換,從而實(shí)現(xiàn)了 Promise 的內(nèi)容按照我們所定義的順序執(zhí)行。
有同學(xué)可能對(duì)這里的 toPromise 方法有一些疑惑,我先把代碼貼出來(lái)
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; }
其實(shí)這個(gè)函數(shù)做的事情就是,根據(jù)不同的類型進(jìn)行轉(zhuǎn)換,使得最后輸出的類型都是一個(gè) Promise。那具體的轉(zhuǎn)換細(xì)節(jié),大家可以參考co庫(kù)的源碼。
至此實(shí)現(xiàn)異步操作的控制。
最后這里是 Dendoink ,奇舞周刊原創(chuàng)作者,掘金 [聯(lián)合編輯 / 小冊(cè)作者] 。
對(duì)于技術(shù)人而言:技 是單兵作戰(zhàn)能力,術(shù) 則是運(yùn)用能力的方法。得心應(yīng)手,出神入化就是 藝 。在前端娛樂(lè)圈,我想成為一名出色的人民藝術(shù)家。掃碼關(guān)注公眾號(hào) 前端惡霸 我在這里等你:
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/104337.html
摘要:數(shù)據(jù)的層級(jí)意味著迭代數(shù)據(jù)結(jié)構(gòu)并提取它的數(shù)據(jù)。對(duì)于技術(shù)人而言技是單兵作戰(zhàn)能力,術(shù)則是運(yùn)用能力的方法。在前端娛樂(lè)圈,我想成為一名出色的人民藝術(shù)家。 聊聊 for of 說(shuō)起 for of 相信每個(gè)寫過(guò) JavaScript 的人都用過(guò) for of ,平時(shí)我們用它做什么呢?大多數(shù)情況應(yīng)該就是遍歷數(shù)組了,當(dāng)然,更多時(shí)候,我們也會(huì)用 map() 或者 filer() 來(lái)遍歷一個(gè)數(shù)組。 但是就...
摘要:通過(guò)創(chuàng)建將所有的異步操作邏輯收集在一個(gè)地方集中處理,可以用來(lái)代替中間件。 redux-saga框架使用詳解及Demo教程 前面我們講解過(guò)redux框架和dva框架的基本使用,因?yàn)閐va框架中effects模塊設(shè)計(jì)到了redux-saga中的知識(shí)點(diǎn),可能有的同學(xué)們會(huì)用dva框架,但是對(duì)redux-saga又不是很熟悉,今天我們就來(lái)簡(jiǎn)單的講解下saga框架的主要API和如何配合redux框...
摘要:下面會(huì)從淺到深,淡淡在閱讀源碼過(guò)程中自己的理解。分拆子頁(yè)面后,每一個(gè)子頁(yè)面對(duì)應(yīng)一個(gè)文件。總結(jié)上面就是最早版本的源碼,很簡(jiǎn)潔的使用了等其目的也很簡(jiǎn)單簡(jiǎn)化相關(guān)生態(tài)的繁瑣邏輯參考源碼地址 ??dva的思想還是很不錯(cuò)的,大大提升了開發(fā)效率,dva集成了Redux以及Redux的中間件Redux-saga,以及React-router等等。得益于Redux的狀態(tài)管理,以及Redux-saga中...
閱讀 3356·2023-04-26 03:05
閱讀 1466·2019-08-30 13:09
閱讀 1914·2019-08-30 13:05
閱讀 893·2019-08-29 12:42
閱讀 1390·2019-08-28 18:18
閱讀 3451·2019-08-28 18:09
閱讀 521·2019-08-28 18:00
閱讀 1720·2019-08-26 12:10