万恶的回调
对前端工程师来说,异步回调是再熟悉不过了,浏览器中的各种交互逻辑都是通过事件回调实现的,前端逻辑越来越复杂,导致回调函数越来越多,同时 nodejs 的流行也让 javascript 在后端的复杂场景中得到应用,在 nodejs 代码中更是经常看到层层嵌套。

以下是一个典型的异步场景:先通过异步请求获取页面数据,然后根据页面数据请求用户信息,最后根据用户信息请求用户的产品列表。过多的回调函数嵌套,使得程序难以维护,发展成万恶的回调。

$.get('/api/data', function(data) {console.log(data);$.get('/api/user', function(user) {console.log(user);$.get('/api/products', function(products) {console.log(products)});});
});

异步流程控制
最原始异步流程的写法,就是类似上面例子里的回调函数嵌套法,用过的人都知道,那叫一个酸爽。
后来出现了 Promise ,它极大提高了代码的可维护性,消除了万恶的回调嵌套问题,并且现在已经成为 ES6 标准的一部分。

$.get('/api/data')
.then(function(data) {console.log(data);return $.get('/api/user');
})
.then(function(user) {console.log(user);return $.get('/api/products');
})
.then(function(products) {console.log(products);
});

之后在 nodejs 圈出现了 co 模块,它基于 ES6 的 generator 和 yield ,让我们能用同步的形式编写异步代码。

co(function *() {var data = yield $.get('/api/data');console.log(data);var user = yield $.get('/api/user');console.log(user);var products = yield $.get('/api/products');console.log(products);
});

以上的 Promise 和 generator 最初创造它的本意都不是为了解决异步流程控制。其中 Promise 是一种编程思想,用于“当xx数据准备完毕,then执行xx动作”这样的场景,不只是异步,同步代码也可以用 Promise。而 generator 在 ES6 中是迭代器生成器,被 TJ 创造性的拿来做异步流程控制了。真正的异步解决方案请大家期待 ES7 的 async 吧!本文以下主要介绍 co 模块。
co 模块
上文已经简单介绍了co 模块是能让我们以同步的形式编写异步代码的 nodejs 模块,主要得益于 ES6 的 generator。nodejs >= 0.11 版本可以加 –harmony 参数来体验 ES6 的 generator 特性,iojs 则已经默认开启了 generator 的支持。

要了解 co ,就不得不先简单了解下 ES6 的 generator 和 iterator。

Iterator
Iterator 迭代器是一个对象,知道如何从一个集合一次取出一项,而跟踪它的当前序列所在的位置,它提供了一个next()方法返回序列中的下一个项目。

var lang = { name: 'JavaScript', birthYear: 1995 };
var it = Iterator(lang);
var pair = it.next();
console.log(pair); // ["name", "JavaScript"]
pair = it.next();
console.log(pair); // ["birthYear", 1995]
pair = it.next(); // A StopIteration exception is thrown

乍一看好像没什么奇特的,不就是一步步的取对象中的 key 和 value 吗,for … in也能做到,但是把它跟 generator 结合起来就大有用途了。

Generator
Generator 生成器允许你通过写一个可以保存自己状态的的简单函数来定义一个迭代算法。Generator 是一种可以停止并在之后重新进入的函数。生成器的环境(绑定的变量)会在每次执行后被保存,下次进入时可继续使用。generator 字面上是“生成器”的意思,在 ES6 里是迭代器生成器,用于生成一个迭代器对象。

function *gen() {yield 'hello';yield 'world';return true;
}

以上代码定义了一个简单的 generator,看起来就像一个普通的函数,区别是function关键字后面有个*号,函数体内可以使用yield语句进行流程控制。

var iter = gen();
var a = iter.next();
console.log(a); // {value:'hello', done:false}
var b = iter.next();
console.log(b); // {value:'world', done:false}
var c = iter.next();
console.log(c); // {value:true, done:true}

当执行gen()的时候,并不执行 generator 函数体,而是返回一个迭代器。迭代器具有next()方法,每次调用 next() 方法,函数就执行到yield语句的地方。next() 方法返回一个对象,其中value属性表示 yield 关键词后面表达式的值,done 属性表示是否遍历结束。generator 生成器通过next和yield的配合实现流程控制,上面的代码执行了三次 next() ,generator 函数体才执行完毕。

co 模块思路
从上面的例子可以看出,generator 函数体可以停在 yield 语句处,直到下一次执行 next()。co 模块的思路就是利用 generator 的这个特性,将异步操作跟在 yield 后面,当异步操作完成并返回结果后,再触发下一次 next() 。当然,跟在 yield 后面的异步操作需要遵循一定的规范 thunks 和 promises。

yieldables

The yieldable objects currently supported are:

  • promises thunks (functions)
  • array (parallel execution)
  • objects (parallel execution)
  • generators (delegation)
  • generator functions (delegation)

7行代码
再看看文章开头的7行代码:

function co(gen) {var it = gen();var ret = it.next();ret.value.then(function(res) {it.next(res);});
}

首先生成一个迭代器,然后执行一遍 next(),得到的 value 是一个 Promise 对象,Promise.then() 里面再执行 next()。当然这只是一个原理性的演示,很多错误处理和循环调用 next() 的逻辑都没有写出来。

下面做个简单对比:
传统方式,sayhello是一个异步函数,执行helloworld会先输出”world”再输出”hello”。

function sayhello() {return Promise.resolve('hello').then(function(hello) {console.log(hello);});
}
function helloworld() {sayhello();console.log('world');
}
helloworld();

输出

> "world"
> "hello"

co 的方式,会先输出”hello”再输出”world”。

function co(gen) {var it = gen();var ret = it.next();ret.value.then(function(res) {it.next(res);});
}
function sayhello() {return Promise.resolve('hello').then(function(hello) {console.log(hello);});
}
co(function *helloworld() {yield sayhello();console.log('world');
});

输出

> "hello"
> "world"

消除回调金字塔
假设sayhello/sayworld/saybye是三个异步函数,用真正的 co 模块就可以这么写:

var co = require('co');
co(function *() {yield sayhello();yield sayworld();yield saybye();
});

输出

> "hello"
> "world"
> "bye"

参考
《es7-async》 https://github.com/jaydson/es7-async
《Generator 函数的含义与用法》 http://www.ruanyifeng.com/blog/2015/04/generator.html
《Iterator》 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator

nodejs异步流程控制co 模块相关推荐

  1. nodejs异步流程控制

    场景: 1.图片上传服务器 2.图片写入磁盘 3.图片写入数据库 在nodejs里面这三种方式都是异步操作,不想java其他语言同步进行,这就需要对异步流程进行控制,保证1,2,3逐步进行.有可能1还 ...

  2. 某大型银行深化系统技术方案之八:核心层之异步流程控制机制

    传送门☞Android兵器谱☞转载请注明☞http://blog.csdn.net/leverage_1229 核心层 核心层主要提供后台业务集中处理中最基本.共性的信息处理.流程调度和相关的管理功能 ...

  3. JavaScript异步流程控制的前世今生

    js的流程控制老大难问题就是异步回调. 一个流程过程,往往会出现回调地狱,这个回调异步控制就被提上研究得议程. 目前有实现的回调流程有以下几种 回调函数实现 事件监听 发布订阅 Promise/A+ ...

  4. 流程控制: jQ Deferred 与 ES6 Promise 使用新手向入坑!

    谢谢n͛i͛g͛h͛t͛i͛r͛e͛大大指出的关于Promise中catch用的不到位的错误,贴上大大推荐的文章Promise中的菜鸟和高阶错误,文章很详细说明了一些Promise使用中的错误和指导. ...

  5. Node.js 模块之Nimble流程控制

    NodeJS异步的特性有时候会导致程序非常的难看,回调一层套着一层,这个时候就要用流程控制模块来控制究竟是同步还是异步了. Nimble是一个轻量.可移植的函数式流程控制模块.经过最小化和压缩后只有8 ...

  6. JS_异步任务之流程控制

    需知: 1,JavaScript只有一个核心的主线程,但它有存放异步任务的任务队列(task queue). 2,主线程中是正在运行的同步任务(异步任务开始运行则也会变为同步任务),每次同步任务完成后 ...

  7. python:基础知识—流程控制—函数与模块—数据结构—类与GUI和Turtle—异常处理与文件,概括全书(上万字最详细版)

    这里是一张夜景,给大家放松一下. !!无锡南长街 文章目录 模块一:基础知识 1.python语言 2.常见数字类型 3.字符串 4.数字类型转换 5.标识符命名 6.常见关键字 7.运算符与表达式 ...

  8. 【Python学习笔记—保姆版】第三章—Python流程控制、函数的定义、常见错误、模块与包、类

    第三章-python函数.分支结构 流程控制 if-else for 循环 while循环 continue/break range()函数 列表与元组遍历 练习 函数的定义 变量作用域 基本形式 函 ...

  9. 一周一论文(翻译)——[VLDB 18] Chi:分布式流处理系统下可扩展的、可编程的控制计划模块

    Abstract 流处理工作负载和现代共享集群环境表现出高度的可变性和不可预测性. 结合大量参数空间和各种用户SLO,这使得现代流处理系统非常难以静态配置和调整. 为了解决这些问题,在本文中,我们研究 ...

最新文章

  1. 2.QML组件、图像几何变换和元素定位器
  2. 区块链今年,胜过过去十年
  3. 23 Python常用模块(一)
  4. js与C++交互及C++解析json
  5. Katas编写的Java教程:Mars Rover
  6. C语言变量初始化是必须的
  7. Oracle 分页查询
  8. 八数码宽度优先搜索python代码_图之遍历--广度优先遍历
  9. linux下挂载ntfs分区错误解决方法
  10. 萤石云摄像头方向操控前端代码
  11. libcef(一)编译CEF
  12. POJ 3764 The xor-longest Path
  13. 思科模拟器交换机的几种配置模式
  14. python修改ppt的字体和颜色,PPT小技巧:批量修改文字颜色,批量替换字体
  15. exchange2016卸载报错安装程序无法卸载,因为mscorsvw(9476)具有打开的文件
  16. 2019年9月8日秋季PAT甲级题解A1163(7-4)Dijkstra Sequence
  17. 智慧公寓管理系统解决方案
  18. HTML5七夕情人节表白网页_生日快乐粒子烟花(自定义文字)_ HTML+CSS+JS 求婚 html生日快乐祝福代码网页 520情人节告白代码 程序员表白源码 抖音3D旋转相册 js烟花代码
  19. 2022研究生学术与职业素养讲座(MOOC)期末答案
  20. 【mysql】关闭mysql缓存的方法

热门文章

  1. python读取csv数据画直方图_python 中直方图绘制
  2. 一个小试题:英雄角色PK
  3. Flutter:1个人,100天业余时间,能开发出什么?
  4. 学生管理系统登入页面
  5. Ansoft HFSS v15.0 win32_64 Full-ISO 2DVD(三维结构电磁场仿真软件)
  6. 2021电赛题目预测
  7. 古风男孩美名推荐分享
  8. python filter和map的区别_js中filter和map的区别
  9. 【物联网初探】- 09 - 基于 ESP32 和微信小程序的土壤湿度监测【完结篇】
  10. android之使用QQ互联实现qq账号登录App