异步操作,一个任务不是连续完成的,先执行一段,转而执行其他任务,等做好了准备,再回过头执行第二段。
ES6之前的异步操作,大致以下四种:
- 回调函数
- 事件监听
- 发布/订阅
- Promise对象
回调函数
const fs = require('fs');// 回调函数fs.readFile('./data1.json', 'utf-8', function (err, data) {console.log(err)console.log(data)})// 回调地狱出现 callback hellfs.readFile('./data1.json', 'utf-8', function (err, data) {console.log(err)console.log(data)fs.readFile('data2.json', 'utf-8', function (err2, data2) {console.log(err2)console.log(data2)})})
Promise
Promise的出现是为了解决回调地狱,将函数回调改为链式调用。采用Promise连续读取多个文件。
const readFilePromise = require('fs-readfile-promise')// 返回promise的readFilereadFilePromise('data1.json', 'utf-8').then(function(data) {console.log('readFilePromise-1', data)}).then(function () {return readFilePromise('data21.json', 'utf-8')}).then(function (data) {console.log(data.toString())}).catch(function (err) {console.log(1, err)})
用Promise写要将原来的代码包上一层Promise,会显得有些冗余,同时会出现一堆then的处理。
Generator函数
function* gen(x) {var y = yield x + 2;return y;}var g = gen(1)console.log(g.next())console.log(g.next())
上面的代码中,调用Generator函数会返回一个指针(即遍历器)g,调用g.next方法,指针指向第一个遇到的yield语句。
const readFilePromise = require('fs-readfile-promise');const fs = require('fs')function* asyncJob() {var data = yield readFilePromise('data1.json', 'utf-8');return data}var job = asyncJob();var res1 = job.next()// res1返回{value: Promise{<pending>, done: false}}res1.value.then(function (data) {console.log(data)})var res2 = job.next();console.log(res1)console.log(res2)
Generator函数是协程在ES6的实现,最大的特点就是可以交出函数的执行权(即暂停执行)。
整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用yield语句注明。
Generator函数的数据交换和错误处理。Generator函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。
除此之外,它还有两个特性,使他可以作为异步编程的完整解决方案:
- 函数体内外的数据交换
- 错误处理机制
// 数据交换function* genV2(x) {var y = yield x + 2;return y;}var g2 = genV2(1);console.log(g2.next())console.log(g2.next(20000))
// 错误处理function* genV3(x) {try {// throw new Error()var y = yield x + 2;} catch (e) {console.log(1, e)}}var g3 = genV3(1)console.log(g3.next())console.log(g3.throw('出错了!!'))
