js异步从入门到放弃(四)- Generator 封装异步任务
栏目: JavaScript · 发布时间: 5年前
内容简介:在之前的文章介绍了传统异步的实现方案,本文将介绍ES6中的一种全新的异步方案--Generator函数。简单介绍一下generator的原理和语法,(更详细内容请看通过一个简单的例子来了解
在之前的文章介绍了传统异步的实现方案,本文将介绍ES6中的一种全新的异步方案--Generator函数。
generator简介
简单介绍一下generator的原理和语法,(更详细内容请看 ECMAScript 6 入门 ,本文只介绍和异步相关的核心内容)
基本语法
通过一个简单的例子来了解 generator
函数
function* MyGenerator() { yield 'yield的含义是:执行此处时,暂停执行当前函数' yield '暂停之后的函数可以用next方法继续执行' return '遇到return之后会真正结束,done会变成true' } const gen = MyGenerator() //执行后返回的是一个指针,可以利用该指针执行next方法 console.log(gen.next()) // {value: "yield的含义是:执行此处时,暂停执行当前函数“, done: false} console.log(gen.next())// {value: "暂停之后的函数可以用next方法继续执行", done: false} console.log(gen.next())// {value: "遇到return之后会真正结束,done会变成true", done: true}
数据交互
数据交互指的是 可以通过yield把当前执行的结果传出,也可以使用next把外部参数传入
function* MyGenerator(){ const result = yield '传出的数据' return result } const gen = MyGenerator() console.log(gen.next())// generatorAndAsync2.html:19 {value: "传出的数据", done: false} console.log(gen.next('传入的数据'))// {value: "传入的数据", done: true}
交互的参数类型当然也可以是 函数 :
function sayHi(){ console.log('hi'); } function* MyGenerator(){ const result = yield sayHi() return } const gen = MyGenerator() const result = gen.next()// {value: sayHi, done: false} result.value() // 正常输出'hi'
具备以上最核心的两个特性之后,generator就可以进行异步操作封装。
异步任务封装
首先,结合异步任务的特点以及前文提到的genrator函数的特性,提炼出使用generator封装异步操作的核心思路:
yield next
起步
从一个最简单的例子开始:
// 1. 首先写一个异步任务,在一秒后返回特定字符串 function asyncTask(callback){ setTimeout(()=>{ callback('Hello Leo') }, 1000) } // 2. 接下来写出期望执行的顺序 function* runTask() { let text = yield asyncTask console.log(text) // 我们期望这里正常输出Hello Leo } // 3. 按照期望值执行函数 const gen = runTask()// 此时执行权已经交出 gen.next().value(function (text) {// 执行asyncTask并传入callback ,关键点在于在callback里调用next交还执行权 gen.next(text) })
首先,这段代码虽然很粗糙,但是已经反映了使用 generator
封装异步任务的核心思想。最直观的受益就是: runTask
的内容看起来很像同步代码,条理清晰,很适合阅读。
但是上面第3部分关于执行的代码很不灵活,我们不能每次都这么写一段,因此接下来的目标就是 实现一个任务执行器 。
自动任务执行器
同样的,先思考核心的思路:要想让某个 generator
函数自动执行,无非就是一个 while
循环:
1. 如果当前yield返回值的done属性为true,说明任务已经执行完成; 2. 如果当前yield返回值的done属性为false,说明任务还未执行完成,则继续执行next,同时要注意参数传递
根据分析实现以下的执行器:
function autoExecute(task) { const gen = task() let result = gen.next() while(true){ if(result.done){ break // 执行结束 return } console.log(result.value)//为了便于观察 我们加上console.log result = gen.next(result.value) // 每次都应该重写result 获取最新结果 } } function* simpleTask(){ yield 1 yield 2 yield 3 return } autoExecute(simpleTask)// 测试我们写的自动执行器 能够正确输出123
上面的执行器已经有了雏形,但是对于前面例子中, result.value为函数的情况 还没有处理,因此还需要稍微补充:
function isFunction(source){ return Object.prototype.toString.call(source) === "[object Function]" } function autoExecute(task) { const gen = task() let result = gen.next() let isRuningAsync = false // 由于加入了异步处理,所以要增加一个标志位避免重复进入循环体 while (!isRuningAsync) { if (result.done) { return } console.log(result.value) /* start 补充的处理函数 */ if (isFunction(result.value)) { isRuningAsync = true const callback = (arg) => { result = gen.next(arg) // 核心代码 isRuningAsync = false } result.value(callback) /* end 补充的处理函数 */ } else { result = gen.next(result.value) } } } autoExecute(runTask) // 试着用这个自动执行器执行之前的异步任务
上面这个自动执行器,就完成了generator对异步任务的封装。
总结
本文简要介绍了generator函数的一些特性,重点在于说明如何使用 generator
函数对异步任务进行封装,从而能够让异步代码编写的更加清晰。
再次强调:用 generator
函数对异步任务封装的思想是很明确的-- 控制 Generator 函数的流程,在适当的时机接收和交还程序的执行权,但是具体的实现方式并不唯一,例如本文用的是最简单直接的回调函数方式,在阮一峰老师的《es6入门》教程里,也有使用thunk思路来讲解的部分,可以自行查阅。
以上所述就是小编给大家介绍的《js异步从入门到放弃(四)- Generator 封装异步任务》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 改进异步封装:处理带返回值的异步调用
- Go语言同步与异步执行多个任务封装详解(Runner和RunnerAsync)
- 记一次小程序开发中如何使用async-await并封装公共异步请求
- 封装JDBC—非框架开发必备的封装类
- SpringBlade 2.3.2 发布,增加 OSS 封装及单元测试封装
- SpringBlade 2.3.2 发布,增加 OSS 封装及单元测试封装
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据结构与问题求解
韦斯 / 清华大学出版社 / 2011-8 / 89.50元
《数据结构与问题求解(Java语言版)(第4版)》是专为计算机科学专业的两个学期课程而设计的,从介绍什么足数据结构开始,继而对高级数据结构与算法进行分析。《数据结构与问题求解(Java语言版)(第4版)》以独特的方式,清晰地将每种数据结构的接口与其实现分离开来,即将如何使用数据结构与如何对数据结构编程相分离。《数据结构与问题求解(Java语言版)(第4版)》从抽象思维和问题求解的角度出发,为数据结......一起来看看 《数据结构与问题求解》 这本书的介绍吧!