promise和co搭配生成器函数方式解决js代码异步流程的比较


Posted in Javascript onMay 25, 2018

在es6中引入的原生Promise为js的异步回调问题带来了一个新的解决方式,而TJ大神写的co模块搭配Generator函数的同步写法,更是将js的异步回调带了更优雅的写法。

今天我想对比一下这两种方式,来看看这两种方式的区别以及优劣。

我们先抽象几个操作:

以做饭为例,我们先去买菜,回来洗菜,刷碗,烧菜,最后才是吃。定义如下方法:

var buy = function (){}; //买菜,需要3s
var clean = function(){};  //洗菜,需要1s
var wash = function(){};  //刷碗,需要1s
var cook = function(){};  //煮菜,需要3s
var eat = function () {};  //吃饭,2s,最后的一个步骤。

在实际中,我们可能这样,先去买菜,然后洗菜,然后开始烧菜,烧菜的同时,刷碗,等碗刷完了,菜煮好了,我们才开始吃饭。也就是,煮菜和刷碗是并行的。

Promise方式

var begin = new Date();
buySomething().then((buyed)=>{
  console.log(buyed);
  console.log(new Date()-begin);
  return clean();
}).then((cleaned)=>{
  console.log(cleaned);
  console.log(new Date()-begin);
  return Promise.all([cook(),wash()]);
}).then((value)=>{
  console.log(value);
  console.log(new Date()-begin);
  return eat();
}).then((eated)=>{
  console.log(eated);
  console.log(new Date()-begin);
}).catch((err)=>{
  console.log(err);
});

输出结果:

菜买到啦
3021
菜洗乾?Q了
4023
[ '?菜煮好了,可以吃?了', '?子洗乾?Q啦' ]
7031
?吃完了,好舒服啊
9034

Promise里有个all()方法,可以传递一个promise数组,功能是当所有promise都成功时,才返回成功。上面的例子,我们就将 cook()和wash()放到all()方法,模拟两个操作同时进行。从时间上来看,先去买菜,耗时3s,洗菜耗时1s,输出4023,刷碗和煮菜同时进行,以耗时长的煮菜3s,输出7031,最后吃饭2s,输出9034。

Promise的优势就是,可以随意定制Promise链,去掌控你的流程,想要同步的时候,就使用Promise链,想要异步,就使用Promise.all(),接口也很简单,逻辑也很简单。

co+Generator搭配使用

let begin = new Date();
co(function* (){
  let buyed = yield buySomething();
  console.log(buyed);
  console.log(new Date() - begin);
  let cleaned = yield clean();
  console.log(cleaned);
  console.log(new Date() - begin);
  let cook_and_wash = yield [cook(),wash()];
  console.log(cook_and_wash);
  console.log(new Date() - begin);
  let eated = yield eat();
  console.log(eated);
  console.log(new Date() - begin);
});

输出:

菜买到啦
3023
菜洗乾?Q了
4026
[ '?菜煮好了,可以吃?了', '?子洗乾?Q啦' ]
7033
?吃完了,好舒服啊
9035

从代码上,我们可以看出,使用co+Generator函数的写法,更趋向于同步代码的写法,具体代码是怎么执行的,大家可以研究一下es6的Generator函数。而且yield也可以接收一个数组,用来异步执行两个方法。代码上更精炼,更符合逻辑。
当前来说,co模块+Generator函数是一个比较好的改善“回调地狱”的优美的解决方案。

而且,这种方式比Promise优的一点是,Promise在实际操作中可能需要嵌套,例如我上一篇博客中<>中的例子一样,如果使用co+generator方式,则可以减少嵌套,在代码逻辑上更清晰,也不会让人思维混乱。

如可以改写为如下:

var getColl = (collname, db) => {
 return new Promise(function(resolve,reject){
  co(function* (){
   var coll = yield db.createCollection(collname,{capped: true,size: 11800000,max: 5000});
   var stats = yield coll.stats();
   if(stats.count == 0){
    var inserted = yield coll.insert({coll: collname,create_time: new Date()});
   }
   resolve(coll);
  }).catch(function(err){
   reject(err);
  });
 });
};

这段代码在逻辑上,看起来就比之前用纯Promise实现的清爽些,逻辑上不混乱。

es7的async和await

es7提供了async函数和await,这里和co+Generator函数方式是一样的,都是基于Promise实现的。这里async函数可以看作是Generator函数的语法糖,await可以看作是yield的语法糖。

co模块可以看作是Generator函数的执行器,而es7中,async函数自带执行器,这样就不必引用第三方的co模块了。

更好的语义,async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

更广的适用性。 co模块约定,yield命令后面只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
返回值是Promise。async函数的返回值是Promise对象,这比Generator函数的返回值是Iterator对象方便多了。你可以用then方法指定下一步的操作。

可以说,es7的async和await才是解决回调地狱的终极大招,虽然现在还不能以原生代码编写,但是可以使用es7编写代码,然后使用babel转译成es5代码。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
HTML IMG标签 onload 内存溢出导致浏览器CPU占用过高
Mar 09 Javascript
一个可以兼容IE FF的加为首页与加入收藏实现代码
Nov 02 Javascript
jQuery调用WebMethod(PageMethod) NET2.0的方法
Apr 15 Javascript
Bootstrap 3.x打印预览背景色与文字显示异常的解决
Nov 06 Javascript
jQuery简单获取DIV和A标签元素位置的方法
Feb 07 Javascript
一次围绕setTimeout的前端面试经验分享
Jun 15 Javascript
深入理解vue.js中$watch的oldvalue与newValue
Aug 07 Javascript
利用Three.js如何实现阴影效果实例代码
Sep 26 Javascript
详解Vue.js项目API、Router配置拆分实践
Mar 16 Javascript
JS中Promise函数then的奥秘探究
Jul 30 Javascript
jquery实现垂直手风琴菜单
Mar 04 jQuery
JS + HTML 罗盘式时钟的实现
May 21 Javascript
JS简单生成由字母数字组合随机字符串示例
May 25 #Javascript
Webpack中雪碧图插件使用详解
May 25 #Javascript
使用javascript做在线算法编程
May 25 #Javascript
JS实现的汉字与Unicode码相互转化功能分析
May 25 #Javascript
浅析node.js的模块加载机制
May 25 #Javascript
webpack4的迁移的使用方法
May 25 #Javascript
最后说说Vue2 SSR 的 Cookies 问题
May 25 #Javascript
You might like
杏林同学录(九)
2006/10/09 PHP
php获取文件夹路径内的图片以及分页显示示例
2014/03/11 PHP
在Yii框架中使用PHP模板引擎Twig的例子
2014/06/13 PHP
PHP中__FILE__、dirname与basename用法实例分析
2014/12/01 PHP
一些Javascript的IE和Firefox(火狐)兼容性的问题总结及常用例子
2009/05/21 Javascript
Jquery Ajax学习实例6 向WebService发出请求,返回DataSet(XML) 异步调用
2010/03/18 Javascript
js创建子窗口并且回传值示例代码
2013/07/02 Javascript
jquery选择器、属性设置用法经验总结
2013/09/08 Javascript
在JavaScript里防止事件函数高频触发和高频调用的方法
2014/09/06 Javascript
Javascript从数组中随机取出不同元素的两种方法
2016/09/22 Javascript
微信小程序 video组件详解
2016/10/25 Javascript
bootstrap+jQuery 实现下拉菜单中复选框全选和全不选效果
2017/06/12 jQuery
史上最全JavaScript数组去重的十种方法(推荐)
2017/08/17 Javascript
基于 Immutable.js 实现撤销重做功能的实例代码
2018/03/01 Javascript
JavaScript实现数组全排列、去重及求最大值算法示例
2018/07/30 Javascript
JS实现方形抽奖效果
2018/08/27 Javascript
jQuery超简单遮罩层实现方法示例
2018/09/06 jQuery
深入浅析Vue.js 中的 v-for 列表渲染指令
2018/11/19 Javascript
Vue的双向数据绑定实现原理解析
2020/02/17 Javascript
vue实现移动端拖动排序
2020/08/21 Javascript
Vue前端判断数据对象是否为空的实例
2020/09/02 Javascript
JavaScript 防抖和节流遇见的奇怪问题及解决
2020/11/20 Javascript
在vue中动态修改css其中一个属性值操作
2020/12/07 Vue.js
python使用urlparse分析网址中域名的方法
2015/04/15 Python
使用Python将字符串转换为格式化的日期时间字符串
2019/09/01 Python
python模块hashlib(加密服务)知识点讲解
2019/11/25 Python
keras处理欠拟合和过拟合的实例讲解
2020/05/25 Python
宝塔面板出现“open_basedir restriction in effect. ”的解决方法
2021/03/14 PHP
详解WebSocket跨域问题解决
2018/08/06 HTML / CSS
使用iframe+postMessage实现页面跨域通信的示例代码
2020/01/14 HTML / CSS
腾讯技术类校园招聘笔试试题
2014/05/06 面试题
工程造价与财务管理专业应届生求职信
2013/10/06 职场文书
2014幼儿园教育教学工作总结
2014/12/17 职场文书
检讨书怎么写
2015/01/23 职场文书
vue+spring boot实现校验码功能
2021/05/27 Vue.js
python实现Nao机器人的单目测距
2021/09/04 Python