小试ES6:异步编程之Generator

Generator

生成器是es6原生提供的异步编程方案,其语法行为和传统函数完全不同,阮大的《ECMAScript 6 入门》一书中对生成器有比较详尽的介绍,可以参考,还有一些其他的文章比如:

本文主要是通过一些代码示例来记录和总结生成器的用法。

yield 和 next

yieldnext在生成器中扮演着非常重要的角色,前者是一个操作符,后者是生成器上的一个函数。

他们具有以下特性:

  • 需要调用generator的next函数,生成器中的语句才开始执行;
  • next函数在生成器之外调用,意味着可以在生成器之外控制生成器内部的操作的执行过程;
  • 当生成器遇到yield操作符就立即执行yield之后的语句并暂停,不敢妄言内部原理,姑且感性地比作savepoint;
  • 当再次调用生成器的next函数时,生成器从上次发生yield‘savepoint’继续执行,直到再次遇到yield,或者遇到是return或者throw生成器就退出;
  • next的输入参数在上一次发生yield的地方返回,所以第一次调用next传入的参数没有卵用;
  • next的返回值是一个形如{done:false, value:x}的对象,每次执行next都会使生成器继续执行到下一条yieldnext的返回值中的value属性是紧接在这条yield之后的语句执行之后的返回值,如果遇到return 或者再也没有yield操作那么返回对象中done=truevalue则是return的返回值(没有return时返回undefined);

以上说了很多,先利用next的返回值特性容实现一个无限的斐波那契数列,他永远不会返回done=true

const f = function* fibonacci() {
    let [a, b] = [0, 1];

    for (;;) {
        yield a;
        [a, b] = [b, a + b];
    }
}();

//执行三次,得到0,1,1
for (let i of Array(3).keys()) {
    console.log(f.next());
}

接下来通过一段代码看看next和yield在传值和返回值上的情况,如下:


const iter = function* gen() {
    console.log(`yield ${(yield 'a' + 0)}`);
    console.log(`yield ${(yield 'b' + 1)}`);
    return 'c' + 2;
}();

console.log(`next:${iter.next(0).value}`);  //输出 next:a0
console.log(`next:${iter.next(1).value}`);  //输出 yield 1 next:b1
console.log(`next:${iter.next(2).value}`);  //输出 yield 2 next:c2
  1. 第一个next触发生成器执行到第一个yield,并立即执行'a' + 0 = 'a0', a0作为这次next的返回值
  2. 第二个带入参为1的next触发生成器执行到第二个yield,此时第一个yield返回1,并执行到下一条yield,立即这条yield后面你的'b' + 1 = 'b1'b1作为这次next的返回
  3. 第三个next执行以此类推……

异步编程方案

在同步编程模型中,每个函数总是有序依次地执行,一般上一个函数执行的结果往往是下一个函数的入参,那么在javascript中如何让下一个异步操作等待上一个异步执行得到结果之后在执行呢?

我们已经知道next可以触发生成器执行到yield操作处,并且生成器会在遇到yield时立即执行后面的语句并暂停,那么如果yield后面是一个异步操作,而异步操作获取到结果之后再调用next不就实现了等待的效果么?

function asyncfuc(v) {
    setTimeout(function() {
        let r = v + 20;
        console.log(r);
        g.next(r); //把本次的结果传出并触发下一个yield
    }, 500);
}


let g = function* gen() {
    let v1 = yield asyncfuc(0);
    let v2 = yield asyncfuc(v1);
    return v2;
}();

g.next();

异步操作执行链

有了前文的基础我们可以实现一个用来执行多个异步操作的函数,定义一个run(...functions)方法依次执行传入的函数,如下:

//这个方法用来模拟一个异步调用
function delay(time, callback) {
  setTimeout(function () {
    callback(`slept for ${time}`);
  }, time);
}

function run(...functions) {
    //构造一个生成器循环执行传入的方法
    var generator = function* sync(resume, functions) {
        let result;
        for (var func of functions) {
            result = yield func(result, resume); //前一个方法执行的结果作为下一个方法的入参
        }
        return result;
    }(resume, functions);

    //提供一个方法用于推进生成器执行。
    function resume(callbackValue) {
        generator.next(callbackValue);
    }
    generator.next(); //触发生成器立即执行第一个方法
}

//模拟异步方法调用, 斐波那契数列
function d(result, resume) {
    delay(1000, (msg) => {
        let value = result;
        if (value) {
            [value.a, value.b] = [value.b, value.a + value.b];
        } else {
            value = { a: 0, b: 1 };
        }
        console.log(value.a);
        resume(value);
    });
    return result;
}

run(d, d, d); //顺序执行异步方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值