Javascript异步编程(二: Generator函数)
来源:互联网 发布:淘宝铝合金箱子 编辑:程序博客网 时间:2024/06/04 01:25
协程概念
传统的编程语言,早有异步编程的解决方案(其实是多任务的解决方案)。其中有一种叫做”协程”(coroutine),意思是多个线程互相协作,完成异步任务。
协程有点像函数,又有点像线程。它的运行流程大致如下。
维基百科
https://en.wikipedia.org/wiki/Coroutine
协程有点像函数,又有点像线程。它的运行流程大致如下。
第一步,协程A开始执行。第二步,协程A执行到一半,进入暂停,执行权转移到协程B。第三步,(一段时间后)协程B交还执行权。第四步,协程A恢复执行。
上面流程的协程A,就是异步任务,因为它分成两段(或多段)执行。
让我举个列子
function asnycJob() { //TODO ...其他代码 const f = yield readFile(fileA); //TODO ...其他代码}
上面代码的函数 asyncJob 是一个协程,它的奥妙就在其中的 yield 命令。它表示执行到此处,执行权将交给其他协程。也就是说,yield命令是异步两个阶段的分界线。
协程遇到 yield 命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。它的最大优点,就是代码的写法非常像同步操作,如果去除yield命令,简直一模一样。
Generator函数的概念
Generator 函数是 ES6 提供的一种异步编程解决方案。
Generator 函数有多种理解角度。从语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,遍历器对象(Iterator Object)。
也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
是不是看着一脸懵逼状态,当然概念永远是抽象的,我举个例子
function* f(i) { i++; yield i; i++; console.log('中间'); yield i; return 'finish';}for(let v of f(i)){ console.log(v);}
你们猜猜看返回结果是什么?
2中间3
猜对了嘛?
我来解释下上面代码,首先for..of内部是ES6迭代器,因为函数f返回本身是个迭代器对象,所以可以直接遍历出现。
当程序遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。当再次调用函数时,内部指针会指向上次yield的位置。下一次再从该位置继续向后执行。
Generator 函数封装异步原理
Generator 函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:函数体内外的数据交换和错误处理机制。
(1)Generator 函数的数据交换
next 方法返回值的 value 属性,是 Generator 函数向外输出数据;next 方法还可以接受参数,这是向 Generator 函数体内输入数据。
我们将遍历改成用next()来调用迭代器对象,
首先封装函数
function* gen(x){ let y = yield x + 2; return y;}let g = gen(1);
常规调用next代码如下
console.log(g.next()) // { value: 3, done: false }console.log(g.next()) // { value: undefined, done: true }
当给next传入参数代码如下。
console.log(g.next()) // { value: 3, done: false }console.log(g.next(99))// { value: 99, done: true }
上面代码中,第一个 next 方法的 value 属性,返回表达式 x + 2 的值(3)。
第二个 next 方法带有参数99,这个参数可以传入 Generator 函数,作为上个阶段异步任务的返回结果,被函数体内的变量 y 接收。因此,这一步的 value 属性,返回的就是2(变量 y 的值)。
遍历器对象的next方法的运行逻辑如下。
(1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
(3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。
换言之,next 方法的作用是分阶段执行 Generator 函数。每次调用 next 方法,会返回一个对象,表示当前阶段的信息( value 属性和 done 属性)。value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。
需要注意的是
yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为 JavaScript 提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
函数f如果是普通函数,在为变量generator赋值时就会执行。但是,函数f是一个 Generator 函数,就变成只有调用next方法时,函数f才会执行。
另外需要注意,yield表达式只能用在 Generator 函数里面,用在其他地方都会报错。
(2) Generator 函数的错误处理
Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。
function* gen(x){ try { var y = yield x + 2; } catch (e){ console.log(e); } return y;}var g = gen(1);console.log(g.next());g.throw('出错了');
输出结果
{ value: 3, done: false }出错了
上面代码的最后一行,Generator 函数体外,使用指针对象的 throw 方法抛出的错误,可以被函数体内的 try … catch 代码块捕获。
这意味着,出错的代码与处理错误的代码,实现了时间和空间上的分离,这对于异步编程无疑是很重要的。
Generator 函数实战
下面看看如何使用 Generator 函数,执行一个真实的异步任务。
const fetch = require('node-fetch');function* gen(){ const url = 'https://api.github.com/users/github'; const result = yield fetch(url); console.log(result['avatar_url']);}
Generator 函数封装了一个异步操作,该操作先读取一个远程接口,然后从 JSON 格式的数据解析信息。就像前面说过的,这段代码非常像同步操作,除了加上了 yield 命令。
执行这段代码的方法如下。
const g = gen();const result = g.next();result.value.then((data) =>{ return data.json();}).then((data)=>{ g.next(data);});
上面代码中,首先执行 Generator 函数,获取遍历器对象,然后使用 next 方法(第二行),执行异步任务的第一阶段。由于 Fetch 模块返回的是一个 Promise 对象,因此要用 then 方法调用下一个next 方法。
可以看到,虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。后面部分,就将介绍如何自动化异步任务的流程管理。
- Javascript异步编程(二: Generator函数)
- Javascript的异步编程(下)及es6之generator
- 学习JavaScript中的异步Generator
- 学习JavaScript中的异步Generator
- JavaScript--Generator函数
- Javascript异步编程(三: Thunk函数)
- Javascript异步编程(五: Boss[async 函数])
- 异步javascript,callback、Promise?我们用Generator!
- 【ES6学习】— (2)异步编程Generator函数和Promise对象简介
- javascript 异步编程javascript
- 小试ES6:异步编程之Generator
- ES6 -- 异步编程神器2:Generator
- Iterator、Generator、async、await 异步编程
- Javascript编程笔记(二)函数
- JavaScript函数式编程(二)
- javascript 异步编程
- javascript 异步编程2
- JavaScript异步编程
- 位运算符&移位运算符
- Springboot系列:Springboot与Thymeleaf模板引擎整合基础教程(附源码)
- 数组小技巧
- 2017 ACM-ICPC 西安赛区 网络赛 F. Trig Function 【规律题】
- bzoj 2618(计算几何+半平面交)
- Javascript异步编程(二: Generator函数)
- 时间和日期类(III)
- 使用Mac+java+selenium进行web自动化测试
- Python基础学习<八>
- 西安网络赛 F 题 Trig Function 【数学公式题】
- 成长之路
- 基于小波变换的时间序列预测,Python实现,来自雪球,
- 算法学习之递归--4个数组成24的问题
- 函数大全