/*
异步编程对 Javascript 语言来说非常重要。
Javascript 语言的执行环境是 “单线程” 的,如果没有异步编程,根本无法使用,不然会造成卡死。
本章主要介绍 Generator 函数如何完成异步操作
*/
/*
17.1 传统方法
es6 诞生以前,异步编程的方法大概有下面 4 种
+ 回调函数
+ 事件监听
+ 发布/订阅
+ Promise 对象
Generator 函数将 Javascript 异步编程带入了一个全新的阶段
*/
/*
17.2 基本概念
17.2.1 异步
所谓 “异步”,简单来说就是一个任务不是连续完成的,可以理解成该任务被人分成两段,
先执行第一段,然后转而执行其他任务,等做好准备后再回过头执行第二段。
比如,有一个任务是读取文件进行处理,任务的第一段是向操作系统发出请求,要求读取文件。
然后,程序执行其他任务,等到操作系统返回文件后再接着执行任务的第二段(处理文件)。
这种不连续的执行就叫作异步。
相应地,连续执行叫作同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能等待。
17.2.2 回调函数
Javascript 语言对异步编程的实现就是回调函数。
所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务时便直接调用这个函数。
回调函数的英文名字 callback,直译过来就是 “重新调用”
*/
// 读取文件进行处理的代码如下
fs.readFile('/etc/passwd', 'utf-8', function(err, data) {
if(err) throw err
console.log(data)
})
// 上面的代码中,readFile 函数的第三个参数就是回调函数,也就是任务的第二段。等到操作系统返回 /etc/passwd 文件以后,回调函数才会执行
/*
Promise
回调函数本身并没有问题,它的问题出现在多个回调函数嵌套上。假定读取 A 文件之后再读取 B 文件,代码如下:
*/
fs.readFile(FileReaderA, 'utf-8', function(err, data) {
fs.readFile(fileB, 'utf-8', function(err, data) {
// ...
})
})
/*
不难想象,如果依次读取以上两个文件,就会出现多重嵌套。
代码不是纵向发展,而是横向发展,很快就会乱成一团,无法管理。
因为多个异步操作形成了强耦合,只要有一个操作需要修改,它的上层回调函数和下层回调函数就都要跟着修改。
这种情况就称为 “回调函数地狱 (callback hell)”
Promise 对象就是为了解决这个问题而被提出来的。
它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套改写成链式调用。采用 Promise 连续读取多个文件的写法如下:
*/
var readFile = require('fs-readfile-promise')
readFile(fileA)
.then(function (data) {
console.log(data.toString())
})
.then(function () {
return readFile(fileB)
})
.then(function (data) {
console.log(data.toString())
})
.catch(function (err) {
console.log(err)
})
/*
上面的代码中,笔者使用了 fs-readfile-promise 模块,它的作用就是返回一个 Promise 版本的 readFile 函数。
Promise 提供 then 方法加载回调函数,catch 方法捕捉执行过程中抛出的错误。
可以看到,Promise 的写法只是回调函数的改进,使用 then 方法以后,异步任务的两段执行更清楚了,除此之外,并无新意。
Promise 的最大问题是代码冗余,原来的任务被 Promise 包装之后,无论什么操作,一眼看去都是许多 then 的堆积,原来的语义变得很不清楚
*/
/*
17.3 Generator 函数
17.3.1 协程
传统的编程语言中早有异步编程的解决方案(其实是多任务的解决方案),其中一种叫作 “协程”,意思是多个线程互相协作,完成异步任务
协程有点像函数,又有点像线程。它的运行流程大致如下:
+ 第一步:协程 A 开始执行
+ 第二步:协程 A 执行到一半,进入暂停状态,执行权转移到协程 B 中
+ 第三步:(一段时间后)协程 B 交还执行权
+ 第四步:协程 A 恢复执行
上面流程的协程 A 就是异步任务,因为它分为两段(或多段)执行
举例来说,读取文件的协程写法如下:
*/
function *asyncJob() {
// ...其他代码
var f = yield readFile(fileA)
// ...其他代码
}
/*
上面的代码的函数 asyncJob 是一个协程,它的奥妙在于其中的 yield 命令。
它表示执行到此处时,执行权将交给其他协程。也就是说,yield 命令是异步两个阶段的分界线。
协程遇到 yield 命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。
它的最大优点是,代码的写法非常像同步操作,如果去除 yield 命令,几乎一摸一样。
*/
17.1、Generator函数的异步应用
最新推荐文章于 2023-07-29 19:19:54 发布