js yield

首先请原谅我的标题党(●—●),tj 大神的 co 模块源码200多行,显然不是我等屌丝能随便几行代码就能重写的。只是当今大家都喜欢《7天学会xx语言》之类的速效仙丹,于是我也弄个类似的名字《7行代码学会co模块》来博眼球。
为了避免被拖出去弹小JJ,还是先放出所谓的 7 行代码给大家压压惊:
function co(gen) {
var it = gen();
var ret = it.next();
ret.value.then(function(res) {
it.next(res);
});
}
万恶的回调
对前端工程师来说,异步回调是再熟悉不过了,浏览器中的各种交互逻辑都是通过事件回调实现的,前端逻辑越来越复杂,导致回调函数越来越多,同时 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
本站专栏文章皆为原创,转载请注明出处(带有 前端乱炖 字样)和本文的显式链接(http://www.html-js.com/article/2908),本站和作者保留随时要求删除文章的权利!
js yield的更多相关文章
- python使用selenium爬百度文库ppt并生成pdf
详细的讲解我是写在另外一个网址:https://www.yuque.com/docs/share/aacfa45c-22c5-4ef6-be97-cd6849002274 有点尬尴,所以就..... ...
- js 中的yield
yield是什么 yield是ES6的新关键字,使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者.它可以被认为是一个基于生成器的版本的return关键字. yield关键字实 ...
- js异步请求发展史和yield
万恶的回调 对前端工程师来说,异步回调是再熟悉不过了,浏览器中的各种交互逻辑都是通过事件回调实现的,前端逻辑越来越复杂,导致回调函数越来越多,同时 nodejs 的流行也让 javascript 在后 ...
- JS Generator yield
function show() { console.log('a') console.log('b') } show() // 普通函数 function *show2() { console.log ...
- js generator和yield
function co<T>(fn: () => Generator<any, any, any>): Promise<T> { const g: Gener ...
- 你不知道的JS 中——yield
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 深入解析js异步编程利器Generator
我们在编写Nodejs程序时,经常会用到回调函数,在一个操作执行完成之后对返回的数据进行处理,我简单的理解它为异步编程. 如果操作很多,那么回调的嵌套就会必不可少,那么如果操作非常多,那么回调的嵌套就 ...
- Node.js实现RESTful api,express or koa?
文章导读: 一.what's RESTful API 二.Express RESTful API 三.KOA RESTful API 四.express还是koa? 五.参考资料 一.what's R ...
- JS魔法堂:深究JS异步编程模型
前言 上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...
随机推荐
- JS 创建长度为100的数组,数值为角标
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- ubuntu16.04给普通用戸提成root权限,会出现造成重启系统,没有登录用户
一.导致问题的原因 直接修改配置文件提权,会造成重启系统后没有原来的登录用户 vim /etc/passwd nulige:x:0:0:nulige,,,:/home/gree:/bin/bash 解 ...
- Linux设备驱动之mmap设备操作
1.mmap系统调用 void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset); 功能:负责把文件内容 ...
- 【招聘App】—— React/Nodejs/MongoDB全栈项目:socket.io&聊天实现
前言:最近在学习Redux+react+Router+Nodejs全栈开发高级课程,这里对实践过程作个记录,方便自己和大家翻阅.最终成果github地址:https://github.com/66We ...
- jQuery:多个AJAX/JSON请求相应单个回调
原文链接:jQuery: Multiple AJAX and JSON Requests, One Callback 原文日期: 2014年4月15日 翻译日期: 2014年4月22日 翻译人员: 铁 ...
- Robomongo与MongoDB的故事
Robomongo,Mongo可视化工具 哇唔,事实上她是三(阴险脸). 你看你看,界面清新,让人家心旷神怡(害羞).谁还想win+R+mongo呀呀呀?! 哎呀呀,继续···说正事. 在这里···借 ...
- DriverStore文件夹特别大,能删除吗?
DriverStore文件夹特别大,能删除吗? DriverStore\FileRepository文件夹特别大,能删除吗? C:\Windows\System32\DriverStore\FileR ...
- H5网站模板——前台和后台
以下是比较典型的前台或者后台的H5模板: html5优分期大学生分期购物商城模板链接:http://pan.baidu.com/s/1dEUAzBz 密码:j150 红色的五金电气商城网站模板链接:h ...
- 使用caffe 的 python接口测试数据,选定GPU编号
只需要在python脚本中添加两行代码: caffe.set_device(0) #使用第一块显卡 caffe.set_mode_gpu() #设为gpu模式 这样,就可以在默认显卡被占用(第一块显卡 ...
- Atitit.得到网络邻居列表java php c#.net python
Atitit.得到网络邻居列表java php c#.net python 1. 获取workgroup net view /domain1 2. 得到网络邻居列表1 3. 得到机器的ip 通过p ...