Es6系列之generator并发调用

简介:

今天来说说generator之间的并发调用,分析生成器之间相互协调处理的功能.类似于其实语言里的协程概念.

此篇文章参考老外的一篇文章: 传送门:)

下面是Generator系列的相关文章链接


CSP

这里先说下CSP(Communicating Sequential Processes),为什么说这个呢,因为它是协程的核心思想

CSP代表程序里的子程序之间能够有条不紊的同时进行,而且能够相互合作完成一件事件,这里有几个关键点:

  • Communicating 通信
  • Sequential 序列化
  • Processes 处理

但是因为js本身是单线程的,那么怎么在generator里实现并发以及相互传递呢,这里只有模拟这种效果.

共享变量

我们可以利用多个generator引用同一个变量,从而解决通信问题,只要保证同时只有一个generator来调用它就行,不过这个在单线程里完全不是问题.

因为要保证多个generator看起来是'并发'执行的,所以需要一种机制来检查,怎么控制在一个generator里跳到另外一个generator里去执行,就像操作系统里的CPU时间片一样,多个任务轮流的共享cpu的时间,这里也可以通过共享变量来做为标识,假如generator内部碰到这个标识那么就自动的切换到另一个generator.

不过这里又有一个切换的问题,我这里是这样的切换机制,按顺序切换,到最后了就自动切会第一个,所以这里也需要一个保存所有generator实例的地方.

下面我们看一个简单的例子

一个简单的协程例子

先定义两个可以实现并发的两个generator

function multBy20(v){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve(v * 20); 
        }, 500);
    });
}

function addTo2(v){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve(v + 2); 
        }, 200);
    });
}

function *foo(token) {
    var value = token.messages.pop(); // 2
    token.messages.push( yield multBy20( value ) );
    // 这是一个协作的标志,代表转移到其它的generator
    yield token;
    yield "meaning of life: " + token.messages[0];
}

function *bar(token) {
    var value = token.messages.pop(); // 40
    token.messages.push( yield addTo2( value ) );
    // 这是一个协作的标志,代表转移到其它的generator
    yield token;
}

上面代码定义了两个generator,两个函数的参数token最终会引用同一个对象,最后要实现的功能就是这样的

foo先从共享变量弹出数字2,利用multBy20得到40,然后添加到共享变量的消息数组里去,碰到token标识,切换到bar里去处理消息数组,弹出40,利用addTo2得到结果42,添加到消息数组里去,碰到token标识,切换到foo里去完成收尾操作.

上面两个generator是交替执行的,哪个先完成就返回哪个的内容,运行generator的代码跟上一篇文章里的差不多,只是这里扩展了generator切换的逻辑.

// 模拟其它语言里的协程概念
function runGenerator(){
    var its, ret, callback,last,cache = {}, cur = 0;
    var token = {};
    token.messages = [];
    token.messages.push(number);
    // 运行generator内部yield的函数
    function iterate(val){
        last = ret;
        ret = cache[cur].next(val);
        console.log('inner:', cur);
        if(!ret.done){
            if(ret.value == token){
                // 切换到另一个generator
                if(cur == (its.length - 1)){
                    cur--;
                }else{
                    cur++;
                    cache[cur] = its[cur](token);
                }
                iterate();
            }
            // 检查是否是promise对象
            if('then' in ret.value){
                ret.value.then(iterate);
            }else{
                setTimeout(function(){
                    iterate(ret.value);
                }, 0)
            }
        }else{
            setTimeout(function(){
                callback.call(null, last || ret);
            }, 0);
        }
    };
    // 包括操作的一个实例对象
    var instance = {
        val: function(fn){
            callback = fn;
        },
        run: function(){
            its = [].slice.call(arguments);
            cache[cur] = its[cur](token);
            iterate();
            return instance;
        }
    }
    return instance;
}

运行代码如下:

// 开始运行
runGenerator(2).run(foo, bar).val(function(msg){
    console.log(msg);
});

运行效果图如下:

现在我们来简单的分析上面的代码

  • token是一个共享变量,作为消息通道的载体以及切换generator的标识
  • cache里保存所有运行的generator实例
  • iterate为核心的运行generator实例的方法,主要负责处理异步返回值的情况以及切换generator
  • instance为内部返回实例,用来作为链式调用的上下文

总结

其实利用generator模拟的并发调用功能可以用来做很多别的有趣事情,已有很多框架利用它来构建基础功能,比较有名的就是nodejs里的koajs.

想了解generator并发例子的可以参考文章头部那个老外链接.


    目录
    相关文章
    |
    5月前
    |
    前端开发
    ES6之生成器(Generator)
    生成器(Generator)是ES6引入的一种特殊的函数,它可以通过yield关键字来暂停函数的执行,并返回一个包含value和done属性的对象。生成器的概念、作用和原理如下所述:
    61 0
    |
    5月前
    ES6 Generator 函数
    ES6 Generator 函数
    |
    2月前
    |
    Go API
    Go 利用上下文进行并发计算
    Go 利用上下文进行并发计算
    |
    10月前
    |
    前端开发
    ES6学习(十)—async 函数
    ES6学习(十)—async 函数
    |
    10月前
    |
    JavaScript
    Generator函数自动执行器
    Generator函数自动执行器
    45 0
    |
    10月前
    ES6学习(九)—Generator 函数的语法
    ES6学习(九)—Generator 函数的语法
    |
    JavaScript 前端开发 Java
    【ES6】Generator函数详解
    【ES6】Generator函数详解
    132 0
    es6 generator 生成器学习总结 使用生成器实现异步请求, async await 的前身
    es6 generator 生成器学习总结 使用生成器实现异步请求, async await 的前身
    |
    存储 索引
    ES中 Nested 类型的原理和使用
    ES中 Nested 类型的原理和使用