第一部分,ES6 中的 Generator

原文地址 http://www.cnblogs.com/wangfupeng1988/p/6532713.html 未經(jīng)作者允許不得轉(zhuǎn)載~

在 ES6 出現(xiàn)之前,基本都是各式各樣類似Promise的解決方案來處理異步操作的代碼邏輯,但是 ES6 的Generator卻給異步操作又提供了新的思路,馬上就有人給出了如何用Generator來更加優(yōu)雅的處理異步操作。

本節(jié)內(nèi)容概述

  • Generator簡(jiǎn)介

  • Generator最終如何處理異步操作

  • 接下來...

Generator簡(jiǎn)介

先來一段最基礎(chǔ)的Generator代碼

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

function* Hello() {
    yield 100
    yield (function () {return 200})()    return 300}var h = Hello()
console.log(typeof h)  // objectconsole.log(h.next())  // { value: 100, done: false }console.log(h.next())  // { value: 200, done: false }console.log(h.next())  // { value: 300, done: true }console.log(h.next())  // { value: undefined, done: true }

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

在 nodejs 環(huán)境執(zhí)行這段代碼,打印出來的數(shù)據(jù)都在代碼注釋中了,也可以自己去試試。將這段代碼簡(jiǎn)單分析一下吧

  • 定義Generator時(shí),需要使用function*,其他的和定義函數(shù)一樣。內(nèi)部使用yield,至于yield的用處以后再說

  • 執(zhí)行var h = Hello()生成一個(gè)Generator對(duì)象,經(jīng)驗(yàn)驗(yàn)證typeof h發(fā)現(xiàn)不是普通的函數(shù)

  • 執(zhí)行Hello()之后,Hello內(nèi)部的代碼不會(huì)立即執(zhí)行,而是出于一個(gè)暫停狀態(tài)

  • 執(zhí)行第一個(gè)h.next()時(shí),會(huì)激活剛才的暫停狀態(tài),開始執(zhí)行Hello內(nèi)部的語(yǔ)句,但是,直到遇到yield語(yǔ)句。一旦遇到yield語(yǔ)句時(shí),它就會(huì)將yield后面的表達(dá)式執(zhí)行,并返回執(zhí)行的結(jié)果,然后又立即進(jìn)入暫停狀態(tài)。

  • 因此第一個(gè)console.log(h.next())打印出來的是{ value: 100, done: false },value是第一個(gè)yield返回的值,done: false表示目前處于暫停狀態(tài),尚未執(zhí)行結(jié)束,還可以再繼續(xù)往下執(zhí)行。

  • 執(zhí)行第二個(gè)h.next()和第一個(gè)一樣,不在贅述。此時(shí)會(huì)執(zhí)行完第二個(gè)yield后面的表達(dá)式并返回結(jié)果,然后再次進(jìn)入暫停狀態(tài)

  • 執(zhí)行第三個(gè)h.next()時(shí),程序會(huì)打破暫停狀態(tài),繼續(xù)往下執(zhí)行,但是遇到的不是yield而是return。這就預(yù)示著,即將執(zhí)行結(jié)束了。因此最后返回的是{ value: 300, done: true },done: true表示執(zhí)行結(jié)束,無(wú)法再繼續(xù)往下執(zhí)行了。

  • 再去執(zhí)行第四次h.next()時(shí),就只能得到{ value: undefined, done: true },因?yàn)橐呀?jīng)結(jié)束,沒有返回值了。

一口氣分析下來,發(fā)現(xiàn)并不是那么簡(jiǎn)單,雖然這只是一個(gè)最最簡(jiǎn)單的Generator入門代碼 ———— 可見Generator的學(xué)習(xí)成本多高 ———— 但是一旦學(xué)會(huì),那將受用無(wú)窮!別著急,跟著我的節(jié)奏慢慢來,一行一行代碼看,你會(huì)很快深入了解Genarator

但是,你要詳細(xì)看一下上面的所有步驟,爭(zhēng)取把我寫的每一步都搞明白。如果搞不明白細(xì)節(jié),至少要明白以下幾個(gè)要點(diǎn):

  • Generator不是函數(shù),不是函數(shù),不是函數(shù)

  • Hello()不會(huì)立即出發(fā)執(zhí)行,而是一上來就暫停

  • 每次h.next()都會(huì)打破暫停狀態(tài)去執(zhí)行,直到遇到下一個(gè)yield或者return

  • 遇到yield時(shí),會(huì)執(zhí)行yeild后面的表達(dá)式,并返回執(zhí)行之后的值,然后再次進(jìn)入暫停狀態(tài),此時(shí)done: false

  • 遇到return時(shí),會(huì)返回值,執(zhí)行結(jié)束,即done: true

  • 每次h.next()的返回值永遠(yuǎn)都是{value: ... , done: ...}的形式

Generator最終如何處理異步操作

上面只是一個(gè)最基本最簡(jiǎn)單的介紹,但是我們看不到任何與異步操作相關(guān)的事情,那我們接下來就先展示一下最終我們將使用Generator如何做異步操作。

之前講解Promise時(shí)候,依次讀取多個(gè)文件,我們是這么操作的(看不明白的需要回爐重造哈),主要是使用then做鏈?zhǔn)讲僮鳌?/p>

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

readFilePromise('some1.json').then(data => {
    console.log(data)  // 打印第 1 個(gè)文件內(nèi)容
    return readFilePromise('some2.json')
}).then(data => {
    console.log(data)  // 打印第 2 個(gè)文件內(nèi)容
    return readFilePromise('some3.json')
}).then(data => {
    console.log(data)  // 打印第 3 個(gè)文件內(nèi)容
    return readFilePromise('some4.json')
}).then(data=> {
    console.log(data)  // 打印第 4 個(gè)文件內(nèi)容})

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

而如果學(xué)會(huì)Generator那么讀取多個(gè)文件就是如下這樣寫。先不要管如何實(shí)現(xiàn)的,光看一看代碼,你就能比較出哪個(gè)更加簡(jiǎn)潔、更加易讀、更加所謂的優(yōu)雅!

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

co(function* () {
    const r1 = yield readFilePromise('some1.json')
    console.log(r1)  // 打印第 1 個(gè)文件內(nèi)容
    const r2 = yield readFilePromise('some2.json')
    console.log(r2)  // 打印第 2 個(gè)文件內(nèi)容
    const r3 = yield readFilePromise('some3.json')
    console.log(r3)  // 打印第 3 個(gè)文件內(nèi)容
    const r4 = yield readFilePromise('some4.json')
    console.log(r4)  // 打印第 4 個(gè)文件內(nèi)容})

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

不過,要學(xué)到這一步,還需要很長(zhǎng)的路要走。不過不要驚慌,也不要請(qǐng)如來佛祖,跟著我的節(jié)奏來,認(rèn)真看,一天包教包會(huì)是沒問題的!

接下來...

接下來我們不會(huì)立刻講解如何使用Generator做異步操作,而是看一看Generator是一個(gè)什么東西!說來話長(zhǎng),這要從 ES6 的另一個(gè)概念Iterator說起。

第二部分,Iterator 遍歷器

ES6 中引入了很多此前沒有但是卻非常重要的概念,Iterator就是其中一個(gè)。Iterator對(duì)象是一個(gè)指針對(duì)象,實(shí)現(xiàn)類似于單項(xiàng)鏈表的數(shù)據(jù)結(jié)構(gòu),通過next()將指針指向下一個(gè)節(jié)點(diǎn) ———— 這里也就是先簡(jiǎn)單做一個(gè)概念性的介紹,后面將通過實(shí)例為大家演示。

本節(jié)演示的代碼可參考這里

本節(jié)內(nèi)容概述

  • 簡(jiǎn)介Symbol數(shù)據(jù)類型

  • 具有[Symbol.iterator]屬性的數(shù)據(jù)類型

  • 生成Iterator對(duì)象

  • Generator返回的也是Iterator對(duì)象

  • 接下來...

簡(jiǎn)介Symbol數(shù)據(jù)類型

Symbol是一個(gè)特殊的數(shù)據(jù)類型,和number string等并列,詳細(xì)的教程可參考阮一峰老師 ES6 入門的 Symbol 篇。先看兩句程序

console.log(Array.prototype.slice)  // [Function: slice]console.log(Array.prototype[Symbol.iterator])  // [Function: values]

數(shù)組的slice屬性大家都比較熟悉了,就是一個(gè)函數(shù),可以通過Array.prototype.slice得到。這里的slice是一個(gè)字符串,但是我們獲取Array.prototype[Symbol.iterator]可以得到一個(gè)函數(shù),只不過這里的[Symbol.iterator]Symbol數(shù)據(jù)類型,不是字符串。但是沒關(guān)系,Symbol數(shù)據(jù)類型也可以作為對(duì)象屬性的key。如下:

var obj = {}
obj.a = 100obj[Symbol.iterator] = 200console.log(obj)  // {a: 100, Symbol(Symbol.iterator): 200}

在此小節(jié)中,你只需要知道[Symbol.iterator]是一個(gè)特殊的數(shù)據(jù)類型Symbol類型,但是也可以像number string類型一樣,作為對(duì)象的屬性key來使用

原生具有[Symbol.iterator]屬性的數(shù)據(jù)類型

在 ES6 中,原生具有[Symbol.iterator]屬性數(shù)據(jù)類型有:數(shù)組、某些類似數(shù)組的對(duì)象(如argumentsNodeList)、SetMap。其中,SetMap也是 ES6 中新增的數(shù)據(jù)類型。

// 數(shù)組console.log([1, 2, 3][Symbol.iterator])  // function values() { [native code] }// 某些類似數(shù)組的對(duì)象,NoeListconsole.log(document.getElementsByTagName('div')[Symbol.iterator])  // function values() { [native code] }

原生具有[Symbol.iterator]屬性數(shù)據(jù)類型有一個(gè)特點(diǎn),就是可以使用for...of來取值,例如

var itemfor (item of [100, 200, 300]) {
    console.log(item)
}// 打印出:100 200 300 // 注意,這里每次獲取的 item 是數(shù)組的 value,而不是 index ,這一點(diǎn)和 傳統(tǒng) for 循環(huán)以及 for...in 完全不一樣

而具有[Symbol.iterator]屬性的對(duì)象,都可以一鍵生成一個(gè)Iterator對(duì)象。如何生成以及生成之后什么樣子,還有生成之后的作用,下文分解。

不要著急,也不要跳過本文的任何步驟,一步一步跟著我的節(jié)奏來看。

生成Iterator對(duì)象

定義一個(gè)數(shù)組,然后生成數(shù)組的Iterator對(duì)象

const arr = [100, 200, 300]
const iterator = arr[Symbol.iterator]()  // 通過執(zhí)行 [Symbol.iterator] 的屬性值(函數(shù))來返回一個(gè) iterator 對(duì)象

好,現(xiàn)在生成了iterator,那么該如何使用它呢 ———— 有兩種方式:nextfor...of

先說第一種,next

console.log(iterator.next())  // { value: 100, done: false }console.log(iterator.next())  // { value: 200, done: false }console.log(iterator.next())  // { value: 300, done: false }console.log(iterator.next())  // { value: undefined, done: true }

看到這里,再結(jié)合上一節(jié)內(nèi)容,是不是似曾相識(shí)的感覺?(額,沒有的話,那你就回去重新看上一節(jié)的內(nèi)容吧) iterator對(duì)象可以通過next()方法逐步獲取每個(gè)元素的值,以{ value: ..., done: ... }形式返回,value就是值,done表示是否到已經(jīng)獲取完成。

再說第二種,for...of

let ifor (i of iterator) {
    console.log(i)
}// 打印:100 200 300

上面使用for...of遍歷iterator對(duì)象,可以直接將其值獲取出來。這里的“值”就對(duì)應(yīng)著上面next()返回的結(jié)果的value屬性

Generator返回的也是Iterator對(duì)象

看到這里,你大體也應(yīng)該明白了,上一節(jié)演示的Generator,就是生成一個(gè)Iterator對(duì)象。因此才會(huì)有next(),也可以通過for...of來遍歷。拿出上一節(jié)的例子再做一次演示:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

function* Hello() {
    yield 100
    yield (function () {return 200})()    return 300 }
const h = Hello()
console.log(h[Symbol.iterator])  // [Function: [Symbol.iterator]]

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

執(zhí)行const h = Hello()得到的就是一個(gè)iterator對(duì)象,因?yàn)?code style="margin: 0px; padding: 0px;">h[Symbol.iterator]是有值的。既然是iterator對(duì)象,那么就可以使用next()for...of進(jìn)行操作

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

console.log(h.next())  // { value: 100, done: false }console.log(h.next())  // { value: 200, done: false }console.log(h.next())  // { value: 300, done: false }console.log(h.next())  // { value: undefined, done: true }let ifor (i of h) {
    console.log(i)
}

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

接下來...

這一節(jié)我們花費(fèi)很大力氣,從Iterator又回歸到了Generator,目的就是為了看看Generator到底是一個(gè)什么東西。了解其本質(zhì),才能更好的使用它,否則總有一種抓瞎的感覺。

接下來我們就Generator具體有哪些使用場(chǎng)景。

 

第三部分,Generator 的具體應(yīng)用

前面用兩節(jié)的內(nèi)容介紹了Generator可以讓執(zhí)行處于暫停狀態(tài),并且知道了Generator返回的是一個(gè)Iterator對(duì)象,這一節(jié)就詳細(xì)介紹一下Generator的一些基本用法。

本節(jié)演示的代碼可參考這里

本節(jié)內(nèi)容概述

  • nextyield參數(shù)傳遞

  • for...of的應(yīng)用示例

  • yield*語(yǔ)句

  • Generator中的this

  • 接下來...

nextyield參數(shù)傳遞

我們之前已經(jīng)知道,yield具有返回?cái)?shù)據(jù)的功能,如下代碼。yield后面的數(shù)據(jù)被返回,存放到返回結(jié)果中的value屬性中。這算是一個(gè)方向的參數(shù)傳遞。

function* G() {
    yield 100}
const g = G()
console.log( g.next() ) // {value: 100, done: false}

還有另外一個(gè)方向的參數(shù)傳遞,就是nextyield傳遞,如下代碼。

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

function* G() {
    const a = yield 100
    console.log('a', a)  // a aaa
    const b = yield 200
    console.log('b', b)  // b bbb
    const c = yield 300
    console.log('c', c)  // c ccc}
const g = G()
g.next()    // value: 100, done: falseg.next('aaa') // value: 200, done: falseg.next('bbb') // value: 300, done: falseg.next('ccc') // value: undefined, done: true

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

捋一捋上面代碼的執(zhí)行過程:

  • 執(zhí)行第一個(gè)g.next()時(shí),為傳遞任何參數(shù),返回的{value: 100, done: false},這個(gè)應(yīng)該沒有疑問

  • 執(zhí)行第二個(gè)g.next('aaa')時(shí),傳遞的參數(shù)是'aaa',這個(gè)'aaa'就會(huì)被賦值到G內(nèi)部的a標(biāo)量中,然后執(zhí)行console.log('a', a)打印出來,最后返回{value: 200, done: false}

  • 執(zhí)行第三個(gè)、第四個(gè)時(shí),道理都是完全一樣的,大家自己捋一捋。

有一個(gè)要點(diǎn)需要注意,就g.next('aaa')是將'aaa'傳遞給上一個(gè)已經(jīng)執(zhí)行完了的yield語(yǔ)句前面的變量,而不是即將執(zhí)行的yield前面的變量。這句話要能看明白,看不明白就說明剛才的代碼你還沒看懂,繼續(xù)看。

for...of的應(yīng)用示例

針對(duì)for...ofIterator對(duì)象的操作之前已經(jīng)介紹過了,不過這里用一個(gè)非常好的例子來展示一下。用簡(jiǎn)單幾行代碼實(shí)現(xiàn)斐波那契數(shù)列。通過之前學(xué)過的Generator知識(shí),應(yīng)該不能解讀這份代碼。

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

function* fibonacci() {
    let [prev, curr] = [0, 1]    for (;;) {
        [prev, curr] = [curr, prev + curr]        // 將中間值通過 yield 返回,并且保留函數(shù)執(zhí)行的狀態(tài),因此可以非常簡(jiǎn)單的實(shí)現(xiàn) fibonacci        yield curr
    }
}for (let n of fibonacci()) {    if (n > 1000) {        break
    }
    console.log(n)
}

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

yield*語(yǔ)句

如果有兩個(gè)Generator,想要在第一個(gè)中包含第二個(gè),如下需求:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

function* G1() {
    yield 'a'
    yield 'b'}function* G2() {
    yield 'x'
    yield 'y'}

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

針對(duì)以上兩個(gè)Generator,我的需求是:一次輸出a x y b,該如何做?有同學(xué)看到這里想起了剛剛學(xué)到的for..of可以實(shí)現(xiàn)————不錯(cuò),確實(shí)可以實(shí)現(xiàn)(大家也可以想想到底該如何實(shí)現(xiàn))

但是,這要演示一個(gè)更加簡(jiǎn)潔的方式yield*表達(dá)式

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

function* G1() {
    yield 'a'
    yield* G2()  // 使用 yield* 執(zhí)行 G2()
    yield 'b'}function* G2() {
    yield 'x'
    yield 'y'}for (let item of G1()) {
    console.log(item)
}

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

之前學(xué)過的yield后面會(huì)接一個(gè)普通的 JS 對(duì)象,而yield*后面會(huì)接一個(gè)Generator,而且會(huì)把它其中的yield按照規(guī)則來一步一步執(zhí)行。如果有多個(gè)Generator串聯(lián)使用的話(例如Koa源碼中),用yield*來操作非常方便。

Generator中的this

對(duì)于以下這種寫法,大家可能會(huì)和構(gòu)造函數(shù)創(chuàng)建對(duì)象的寫法產(chǎn)生混淆,這里一定要注意 —— Generator 不是函數(shù),更不是構(gòu)造函數(shù)

function* G() {}
const g = G()

而以下這種寫法,更加不會(huì)成功。只有構(gòu)造函數(shù)才會(huì)這么用,構(gòu)造函數(shù)返回的是this,而Generator返回的是一個(gè)Iterator對(duì)象。完全是兩碼事,千萬(wàn)不要搞混了。

function* G() {    this.a = 10}
const g = G()
console.log(g.a) // 報(bào)錯(cuò)

接下來...

本節(jié)基本介紹了Generator的最常見的用法,但是還是沒有和咱們的最終目的————異步操作————沾上關(guān)系,而且現(xiàn)在看來有點(diǎn)八竿子打不著的關(guān)系。但是話說回來,這幾節(jié)內(nèi)容,你也學(xué)到了不少知識(shí)啊。

別急哈,即便是下一節(jié),它們還不會(huì)有聯(lián)系,再下一節(jié)就真相大白了。下一節(jié)我們又給出一個(gè)新概念————Thunk函數(shù)

 

第四部分,Thunk 函數(shù)

要想讓Generator和異步操作產(chǎn)生聯(lián)系,就必須過thunk函數(shù)這一關(guān)。這一關(guān)過了之后,立即就可以著手異步操作的事情,因此大家再堅(jiān)持堅(jiān)持。至于thunk函數(shù)是什么,下文會(huì)詳細(xì)演示。

本節(jié)演示的代碼可參考這里

本節(jié)內(nèi)容概述

  • 一個(gè)普通的異步函數(shù)

  • 封裝成一個(gè)thunk函數(shù)

  • thunk函數(shù)的特點(diǎn)

  • 使用thunkify庫(kù)

  • 接下來...

一個(gè)普通的異步函數(shù)

就用 nodejs 中讀取文件的函數(shù)為例,通常都這么寫

fs.readFile('data1.json', 'utf-8', (err, data) => {    // 獲取文件內(nèi)容})

其實(shí)這個(gè)寫法就是將三個(gè)參數(shù)都傳遞給fs.readFile這個(gè)方法,其中最后一個(gè)參數(shù)是一個(gè)callback函數(shù)。這種函數(shù)叫做 多參數(shù)函數(shù),我們接下來做一個(gè)改造

封裝成一個(gè)thunk函數(shù)

改造的代碼如下所示。不過是不是感覺越改造越復(fù)雜了?不過請(qǐng)相信:你看到的復(fù)雜僅僅是表面的,這一點(diǎn)東西變的復(fù)雜,是為了讓以后更加復(fù)雜的東西變得簡(jiǎn)單。對(duì)于個(gè)體而言,隨性比較簡(jiǎn)單,遵守規(guī)則比較復(fù)雜;但是對(duì)于整體(包含很多個(gè)體)而言,大家都隨性就不好控制了,而大家都遵守規(guī)則就很容易管理 ———— 就是這個(gè)道理!

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

const thunk = function (fileName, codeType) {    // 返回一個(gè)只接受 callback 參數(shù)的函數(shù)
    return function (callback) {
        fs.readFile(fileName, codeType, callback)
    }
}
const readFileThunk = thunk('data1.json', 'utf-8')
readFileThunk((err, data) => {    // 獲取文件內(nèi)容})

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

先自己看一看以上代碼,應(yīng)該是能看懂的,但是你可能就是看懂了卻不知道這么做的意義在哪里。意義先不管,先把它看懂,意義下一節(jié)就會(huì)看到。

  • 執(zhí)行const readFileThunk = thunk('data1.json', 'utf-8')返回的其實(shí)是一個(gè)函數(shù)

  • readFileThunk這個(gè)函數(shù),只接受一個(gè)參數(shù),而且這個(gè)參數(shù)是一個(gè)callback函數(shù)

thunk函數(shù)的特點(diǎn)

就上上面的代碼,我們經(jīng)過對(duì)傳統(tǒng)的異步操作函數(shù)進(jìn)行封裝,得到一個(gè)只有一個(gè)參數(shù)的函數(shù),而且這個(gè)參數(shù)是一個(gè)callback函數(shù),那這就是一個(gè)thunk函數(shù)。就像上面代碼中readFileThunk一樣。

使用thunkify庫(kù)

上面代碼的封裝,是我們手動(dòng)來做的,但是沒遇到一個(gè)情況就需要手動(dòng)做嗎?在這個(gè)開源的時(shí)代當(dāng)讓不會(huì)這樣,直接使用第三方的thunkify就好了。

首先要安裝npm i thunkify --save,然后在代碼的最上方引用const thunkify = require('thunkify')。最后,上面我們手動(dòng)寫的代碼,完全可以簡(jiǎn)化成這幾行,非常簡(jiǎn)單!

const thunk = thunkify(fs.readFile)
const readFileThunk = thunk('data1.json', 'utf-8')
readFileThunk((err, data) => {    // 獲取文件內(nèi)容})

接下來...

了解了thunk函數(shù),我們立刻就將Generator和異步操作進(jìn)行結(jié)合

 

第五部分,Generator 與異步操作

這一節(jié)正式開始講解Generator如何進(jìn)行異步操作,以前我們花了好幾節(jié)的時(shí)間各種打基礎(chǔ),現(xiàn)在估計(jì)大家也都等急了,好戲馬上開始!

本節(jié)演示的代碼可參考這里

本節(jié)內(nèi)容概述

  • Genertor中使用thunk函數(shù)

  • 挨個(gè)讀取兩個(gè)文件的內(nèi)容

  • 自驅(qū)動(dòng)流程

  • 使用co庫(kù)

  • co庫(kù)和Promise

  • 接下來...

Genertor中使用thunk函數(shù)