异步的那些事儿,es6

为了解决回调问题,es6引入了原生的迭代器,所以本文主要探讨的是什么是迭代器,迭代器又怎么解决回调问题。

迭代器

迭代器是设计模式的一种,迭代器的核心方法是 hasNextnexthasNext判断函数内部有没有下一个变量,next代表偏移到下一个变量,并且返回结果

迭代器的应用场景在于:屏蔽数据结构集合的各种差异,对于接口只要实现了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());
}

对于日常应用来说,迭代起经常被利用成为遍历数组的工具,如jquery

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

回到es6标准当中,引入了Generator函数,Generator是一个普通函数,但是有两个特征,1,在function命令到函数名之间有一个星号,2.函数内可以使用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语句产生的内部状态。每次调用便利器的next方法时,就会从函数头部或者上一次停下来的地方继续执行。

第一次调用 hello.next() next方法返回一个对象,value是当前yield语句的值1,done属性值false,表示遍历没有结束。

第二次调用,程序将会从yield 1后,继续执行, value是yiled语句抛出的值2, done属性还是false,表示遍历没结束

第三次调用,函数将会从yield 2后,一直执行到return语句,如果没有return语句,函数就直接结束,返回next对象 value 是 undefined, done属性是true,代表遍历已经结束。

总结一下,Generator函数使用iterator接口,每次调用next方法的返回值,就是一个标准的iterator返回值:有着value和done两个属性的对象。其中,value是yield语句后面那个表达式的值,done是一个布尔值,表示是否遍历结束。

迭代器如何解决回调问题

我们先来看看一个经典回调例子代码

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
        });
    });
});

拥有了迭代器的我们,拥有了暂停的功能。

基本解决回调的思路关键在于,充分运用迭代器的next方法,当我们完成了一件任务后,我们运行一下next方法,例如上面例子当然,当我们delay了 200ms的时候,我们调用next,函数将会执行delay 500ms。

说到这里不得不说一个很出名的小函数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 函数,通过 Generator 对象返回的next 和 value 来控制整个异步流,co 函数会 GenFunc 检测 next的情况,如果有可以运行的next的方法,那么就会一直执行下去,下面我们可以看看它的应用

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的匿名函数里面,能够产生类似同步执行的效果,完全消灭了回调,当函数执行完成后,调用 console.timeEnd。

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

后记

  1. 为了配合co模块的使用,需要将迭代器执行的函数,改造成如下格式
function delay(time) {
    return function (cb) {
        setTimeout(function () {
            cb();
        }, time)
    }
}
  1. 有一段时间里面,曾经误认为Generator 是node.js的 新版本特性,而实际上仅仅是v8引擎对于协议的实现而已。

  2. Generator 引入了协程的概念,当在Generator 完成了任务后,将会将函数结果放到内存,能够拥有多个调用栈,当我们执行next的时候,会把结果取出,相对于es5以前的实现,es5只有一个调用栈,es5每次回调出错的时候,调用栈有时候会被冲走。

  3. 关于异步的篇章到目前为止,该算是结束,毫无疑问,es6的解决方案是最优秀的。希望能快点把es6普及起来把

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容

  • 在此处先列下本篇文章的主要内容 简介 next方法的参数 for...of循环 Generator.prototy...
    醉生夢死阅读 1,436评论 3 8
  • 简介 基本概念 Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。本章详细介绍...
    呼呼哥阅读 1,065评论 0 4
  • 官方中文版原文链接 感谢社区中各位的大力支持,译者再次奉上一点点福利:阿里云产品券,享受所有官网优惠,并抽取幸运大...
    HetfieldJoe阅读 6,366评论 9 19
  • 本文作者就是我,简书的microkof。如果您觉得本文对您的工作有意义,产生了不可估量的价值,那么请您不吝打赏我,...
    microkof阅读 23,713评论 16 78
  • 异步编程对JavaScript语言太重要。Javascript语言的执行环境是“单线程”的,如果没有异步编程,根本...
    呼呼哥阅读 7,291评论 5 22