異步的那些事兒,es6

為了解決回調(diào)問題,es6引入了原生的迭代器,所以本文主要探討的是什么是迭代器,迭代器又怎么解決回調(diào)問題。

迭代器

迭代器是設計模式的一種,迭代器的核心方法是 hasNextnexthasNext判斷函數(shù)內(nèi)部有沒有下一個變量,next代表偏移到下一個變量,并且返回結果

迭代器的應用場景在于:屏蔽數(shù)據(jù)結構集合的各種差異,對于接口只要實現(xiàn)了hasNextnext 的api,就可以成功處理各種結果。

var Iterator = function (data) {
    this.curr = 0;
    this.data = data;
}

Iterator.prototype.hasNext = function () {
    return (this.data.length - 1) > curr;
}

Iterator.prototype.next = function () {
    var ret;
    if (!this.hasNext) {
        return;
    }

    ret = this.data[this.curr];
    this.curr += 1;
    return ret;
}

var arrs = [1,2,3,4,5];
var iterator = new Iterator(arrs);

for (var i = 0; i < arrs.length + 1; i++) {
    //1
    //2
    //3
    //4
    //5
    //undefined
    console.log(iterator.next());
}

對于日常應用來說,迭代起經(jīng)常被利用成為遍歷數(shù)組的工具,如jquery

$('li').each(function (index) {
    console.log(index + ': ' + $(this).text());
});

回到es6標準當中,引入了Generator函數(shù),Generator是一個普通函數(shù),但是有兩個特征,1,在function命令到函數(shù)名之間有一個星號,2.函數(shù)內(nèi)可以使用yield語句,通過yield把結果拋出來。

function* Hello () {
    yield 1;
    yield 2;
}

var hello = Hello();

var a = hello.next();
var b = hello.next();
var c = hello.next();

//{ value: 1, done: false } { value: 2, done: false } { value: undefined, done: true }
console.log(a, b, c);

next的方法就是遍歷在yield語句產(chǎn)生的內(nèi)部狀態(tài)。每次調(diào)用便利器的next方法時,就會從函數(shù)頭部或者上一次停下來的地方繼續(xù)執(zhí)行。

第一次調(diào)用 hello.next() next方法返回一個對象,value是當前yield語句的值1,done屬性值false,表示遍歷沒有結束。

第二次調(diào)用,程序將會從yield 1后,繼續(xù)執(zhí)行, value是yiled語句拋出的值2, done屬性還是false,表示遍歷沒結束

第三次調(diào)用,函數(shù)將會從yield 2后,一直執(zhí)行到return語句,如果沒有return語句,函數(shù)就直接結束,返回next對象 value 是 undefined, done屬性是true,代表遍歷已經(jīng)結束。

總結一下,Generator函數(shù)使用iterator接口,每次調(diào)用next方法的返回值,就是一個標準的iterator返回值:有著value和done兩個屬性的對象。其中,value是yield語句后面那個表達式的值,done是一個布爾值,表示是否遍歷結束。

迭代器如何解決回調(diào)問題

我們先來看看一個經(jīng)典回調(diào)例子代碼

function delay(time, cb) {
    setTimeout(function () {
        cb && cb();
    }, time);
}

console.time('1')
delay(200, function () {
    delay(500, function () {
        delay(100, function () {
            console.timeEnd('1');  //1: 804ms
        });
    });
});

擁有了迭代器的我們,擁有了暫停的功能。

基本解決回調(diào)的思路關鍵在于,充分運用迭代器的next方法,當我們完成了一件任務后,我們運行一下next方法,例如上面例子當然,當我們delay了 200ms的時候,我們調(diào)用next,函數(shù)將會執(zhí)行delay 500ms。

說到這里不得不說一個很出名的小函數(shù)co

function co(GenFunc) {
    return function (cb) {
        var gen = GenFunc();
        next();

        function next(args) {
            if (gen.next) {
                var ret = gen.next(args);

                if (ret.done) {
                    cb && cb(args);
                } else {
                    ret.value(next);
                }
            }
        }
    }
}

GenFunc 是 Generator 函數(shù),通過 Generator 對象返回的next 和 value 來控制整個異步流,co 函數(shù)會 GenFunc 檢測 next的情況,如果有可以運行的next的方法,那么就會一直執(zhí)行下去,下面我們可以看看它的應用

function delay(time) {
    return function (cb) {
        setTimeout(function () {
            cb();
        }, time)
    }
}

console.time('1');
co(function* () {
    yield delay(200);
    yield delay(1000);
    yield delay(200);

})(function () {
    console.timeEnd('1');
});

再co的匿名函數(shù)里面,能夠產(chǎn)生類似同步執(zhí)行的效果,完全消滅了回調(diào),當函數(shù)執(zhí)行完成后,調(diào)用 console.timeEnd。

全部示例代碼: https://github.com/youyudehexie/nodejs-cookbook/tree/master/yield

后記

  1. 為了配合co模塊的使用,需要將迭代器執(zhí)行的函數(shù),改造成如下格式
function delay(time) {
    return function (cb) {
        setTimeout(function () {
            cb();
        }, time)
    }
}
  1. 有一段時間里面,曾經(jīng)誤認為Generator 是node.js的 新版本特性,而實際上僅僅是v8引擎對于協(xié)議的實現(xiàn)而已。

  2. Generator 引入了協(xié)程的概念,當在Generator 完成了任務后,將會將函數(shù)結果放到內(nèi)存,能夠擁有多個調(diào)用棧,當我們執(zhí)行next的時候,會把結果取出,相對于es5以前的實現(xiàn),es5只有一個調(diào)用棧,es5每次回調(diào)出錯的時候,調(diào)用棧有時候會被沖走。

  3. 關于異步的篇章到目前為止,該算是結束,毫無疑問,es6的解決方案是最優(yōu)秀的。希望能快點把es6普及起來把

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • 在此處先列下本篇文章的主要內(nèi)容 簡介 next方法的參數(shù) for...of循環(huán) Generator.prototy...
    醉生夢死閱讀 1,463評論 3 8
  • 簡介 基本概念 Generator函數(shù)是ES6提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同。本章詳細介紹...
    呼呼哥閱讀 1,093評論 0 4
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持,譯者再次奉上一點點福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠,并抽取幸運大...
    HetfieldJoe閱讀 6,400評論 9 19
  • 本文作者就是我,簡書的microkof。如果您覺得本文對您的工作有意義,產(chǎn)生了不可估量的價值,那么請您不吝打賞我,...
    microkof閱讀 23,783評論 16 78
  • 異步編程對JavaScript語言太重要。Javascript語言的執(zhí)行環(huán)境是“單線程”的,如果沒有異步編程,根本...
    呼呼哥閱讀 7,336評論 5 22