Es6系列之深入generator2

上一篇文章里深入的了解了generator的异常处理以及函数间的调用,这篇文章主要说说上一篇遗留的一些问题

  • 异步方法的处理

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

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


第一种方案

利用generator来调用异步主要是为了解决异常调用嵌套的问题,下面先模拟一个嵌套调用的异步过程.

functin request(delay, callback){
    setTimeout(function(){
        callback({
            name: 'feenan'
        })
    }, +delay);
}

request(1000, function(msg){
    console.log(msg);
    request(2000, function(msg){
        console.log(msg);
    })
})

然后我们来看看怎么用generator同步处理这里请求,首先我们来定义生成器函数.

function* main(){
    var result = yield request(1000);
    console.log(result);
    var result1 = yield request(2000);
    console.log(result);
}

var it = main();

此时我们需要改进下request函数.

functin request(delay){
    setTimeout(function(){
        it.next({
            name: 'feenan'
        })
    }, +delay);
}

注意上面的异步函数里增加了it.next,这样就能保证回调的值能传入main函数里去.因为next函数的参数是上一次yield表达式的值,第一次给next传参默认会忽略.

上面的方案虽然实现了异步方法的扁平化处理,但是有些缺点

  • 异步方法深度依赖生成器实例,倒致不能重用
  • 不能处理多个异步同时调用的情况

来看看第二种方案是怎么解决这种问题的?

第二种方案promise

虽然上面的方案可以实现异步调用的扁平化处理,但是仍然存在问题,下面我们来用Promise来实现一个更好的扁平化方案

Promisees6提供的原生支持promise规范的类,提供有then,all,catch方法.我们先来改装下request方法

function request(id, delay){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            if(id == '1') reject(new Error('new Error!'));
            resolve({
                name: 'feenan',
                id: id
            });
        }, +delay);
    });
}

Promise通过new一个实例,传入待执行的异步方法,内部利用resolve,reject来导出成功和失败的信息.可以看出这个方法跟generator是完全没关系的,已经解藕了,而且也可以重用在别的类库里,然后我们再来定义我们的生成器函数.

function* main(){
    try{
        var result = yield request('1', 1000);
        if(result.name == 'feenan'){
            var result1 = yield request('2', 2000);
        }
        return 'done!';
    }catch(err){
        console.log(err);
    }

}

此处的try...catch是用来捕获里面异步方法的异常的,通过throw方法,这个等会儿会讲到.

也许大家会看到,上面的main方法的yield会返回一个promise对象,但是result应该会接收到异步回调结果的,这里我们应该怎么处理呢?对,我们还需要定义一个运行生成器函数的函数.

function runGenerator(g){
    return new Promise(function(resolve, reject){
        var it = g(), ret;
        (function iterate(val){
            ret = it.next(val);
            if(!ret.done){
                // 检查是否是promise对象
                if('then' in ret.value){
                    ret.value.then(iterate).catch(function(err){
                        it.throw(err);
                    });
                }else{
                    setTimeout(function(){
                        iterate(ret.value);
                    }, 0)
                }
            }else{
                resolve(ret);
            }
        })();
    });
}

代码看起来有点复杂,我一一来解释.

首先函数本身返回一个promise对象用来处理生成器函数返回值,内部定义了一个iterate立即执行函数,runGenerator内部会先生成一个迭代器名为it,参数即是生成器函数,然后iterate会先调用it.next传递参数,默认第一次参数会忽略,然后检查it.next的返回值,这个返回值属性格式默认为{value: , done: },valueyield后表达式的返回值,done代表迭代是否完毕,因为yield后面返回的是一个promise对象,所以检查ret.value是否是一个promise对象,是的话则调用then方法传递当前iterate本身,这就是运行函数的奥妙所在,因为此时会把异步回调的结果传递给下一次的it.next,这样main函数里的result就能接收到异步回调的结果了,然后就是不断重复上面的步骤直到执行完毕,利用resolve向外抛出生成器函数的返回值,我们来看看运行的样子

runGenerator(main).then(msg){
    console.log(msg); // => done!
}

另外我们还可以利用promise.all方法达到异步同时调用多个方法的功能,此处只需要定义一个多个异步调用方法,promise.all会调用多个promise对象最终只返回一个promise对象

// 包装多个异常请求成功之后返回一个新的promise
// 利用Promise.all类方法
function requestAll(){
    var promises = [1000, 2000, 3000].map(function(v, k){
        return new Promise(function(resolve, reject){
            setTimeout(function(){
                resolve({
                    name: 'feenan',
                    id: v
                })
            }, v);
        });
    });
    return Promise.all(promises);
}

然后我们修改main函数里的request即可

// var result = yield request('1', 1000);
var result = yield requestAll();

此处需要注意下,上面的result是一个数组,里面每项为单个promise对象的返回值

可以看到利用promise+generator可以完美的原生实现异步调用扁平化,这对复杂业务逻辑的实现是相当好的.

下面我再说说es7里对于异步调用方法的更进一步的完美支持,而且代码量更少

第三种方案async

es7里提供了async语法来定义异步函数,配合await关键字轻易就能实现上面的功能

function request(id, delay){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            if(id == '1') reject(new Error('new Error!'));
            resolve({
                name: 'feenan',
                id: id
            });
        }, +delay);
    });
}

async function main() {
    var result1 = await request('1', 1000);
    console.log(result1);
    var result2 = await request('2', 2000);
    console.log(result2);
    console.log( "The value you asked for: " + resp.value );
}

main();

await告诉引擎需要等后面的异步执行完成之后才能执行下面的语句,这是原生支持的,不需要像上面那样定义运行函数来一步一步执行.

await后面必须是一个promise对象实例,所以这里也的用上promise特性.

不过async语法成为标准之路还很遥远,庆幸的是traceur支持这个语法.

总结

最后我觉的还是promisegenerator是完美解决异步调用的方案,以上所有实例都可以通过traceur命令来运行.

时间: 2024-09-20 19:56:58

Es6系列之深入generator2的相关文章

Es6系列之深入generator之异常处理与相互调用

ES6系列之深入Generator 在上一篇文章里简单的介绍了generator的用法,这篇文章主要说说上一篇遗留的一些问题 异常处理 generator之间的调用 此篇文章参考老外的一篇文章: 传送门:). 下面是Generator系列的相关文章链接 Generator基础篇 深入Generator之异常处理与相互调用 深入Generator之异步方法处理 深入Generator之协程处理 异常处理 下面先以一个简单的例子来说明怎么捕获generator里的异常. function * bui

Es6系列之generator基础篇

Ecmascript 6简称es6,是javascript下一代标准,还处在开发阶段,估计2014年底发布,有关更多浏览器对es6的支持情况,点击这里 今天说说es6里新增的Generators. 下面是Generator系列的相关文章链接 Generator基础篇 深入Generator之异常处理与相互调用 深入Generator之异步方法处理 深入Generator之协程处理 Generator generator简单的说就是提供了一种控制函数内部执行状态的功能,以往的普通函数只要开始执行则

Es6系列之module and class

Ecmascript 6简称es6,是javascript下一代标准,还处在开发阶段,估计2014年底发布,有关更多浏览器对es6的支持情况,点击这里 今天说说es6里新增的Module和Class. Class 关于class其实前端已有太多的模拟了,因为js本身的弱类型决定了只要你有想法什么编程模式都可以模拟出来,跟class相关的oop模式早已在后端领域扎根了,前端class概念大多是通过function函数来实现,现在我们来看看es6提供了什么语法?,我们先以下例子来说明,这样比较直观

Es6系列之destructuring assignments

Ecmascript 6简称es6,是javascript下一代标准,还处在开发阶段,估计2014年底发布,有关更多浏览器对es6的支持情况,点击这里 今天说说es6里对赋值语句的改进,简称解构赋值. 解构赋值 所谓解构赋值其实就是按照模式匹配进行批量赋值 下面的是以往的赋值语句 var a = 1; var b = 2; var c = 3; 以往的方式对于赋值多个变量的时候代码比较多而且不方便,那么es6里对它是怎么改进的呢? 通过以对象或者数组结构组装数据进行赋值,要保证赋值左右值类型相同

Es6系列之generator并发调用

今天来说说generator之间的并发调用,分析生成器之间相互协调处理的功能.类似于其实语言里的协程概念. 此篇文章参考老外的一篇文章: 传送门:) 下面是Generator系列的相关文章链接 Generator基础篇 深入Generator之异常处理与相互调用 深入Generator之异步方法处理 深入Generator之协程处理 CSP 这里先说下CSP(Communicating Sequential Processes),为什么说这个呢,因为它是协程的核心思想 CSP代表程序里的子程序之

React-Native学习指南

转发自 : http://www.jianshu.com/p/fd4591a978ba#es6 本指南汇集React-Native各类学习资源,给大家提供便利.指南正在不断的更新,大家有好的资源欢迎Pull Requests! 同时还有Awesome React-Native系列 地址:https://github.com/jondot/awesome-react-native 目录 教程 React Native React.js ES6 系列教程 开源APP 组件 工具 资源网站 业界讨论

布谷技术月刊 1608

布谷技术月刊 1608 干货 Docker学习路线图 Docker学习路线图 如何实现1080P延迟低于500ms的实时超清直播传输技术 详细介绍了视频直播技术 https化的第一步-申请lets-encrypt证书 本文介绍了如何在Let's Encrypt上申请到免费证书 HTTPS 升级指南 本文介绍如何将一个 HTTP 网站升级到 HTTPS 使用 Chrome Timeline 来优化页面性能 Chrome 开发者工具调试性能 常见的网站服务器架构有哪些? 过去十年,编程语言领域有什么

ES6 Features系列:Template Strings & Tagged Template Strings

1. Brief   ES6(ECMAScript 6th edition)于2015年7月份发布,虽然各大浏览器仍未全面支持ES6,但我们可以在后端通过Node.js 0.12和io.js,而前端则通过Traceur或Babel这类Transpiler将ES6语法预转译为ES5语法,来提前兴奋一把.而仅需适配 IE9+的朋友们现在更是可以开始撸ES6了,而不必为学哪门JavaScript超集语言而烦恼.(ES6又名为ECMAScript 2015或JavaScript.next,ES4的部分较

【Xamarin挖墙脚系列:使用Xamarin进行Hybrid应用开发】

原文:[Xamarin挖墙脚系列:使用Xamarin进行Hybrid应用开发] 官方地址:https://developer.xamarin.com/guides/cross-platform/advanced/razor_html_templates/ 使用Xamarin进行网页形式的本地APP开发,感觉有点不爽,不过为前端开发人员提供了开发APP的入口. 呈现引擎支持HTML  和ASP.NET MVC3的Razor引擎! Razor引擎是个好同志! 不过,创建Hybrid应用的框架不仅仅是