javascript在设计之初.为了避免资源管理复杂问题(多个线程同时操作dom,以哪个为准),因此被设计成为了单线程语言.
说起异步就不得不提回调, 为了解决多重回调嵌套导致代码难以维护问题.javascript一直都在完善这个解决方案.
在10多年中javascript 异步流程控制经过了
callback -> event -> promise -> yield & co -> async await
Callback
ES6之前异步编程最常用的方法,如果回调函数嵌套层数较深,代码将变得难以维护.并且在回调函数之外无法捕获回调函数中的异常.
var fs = require('fs')try { fs.readFile('file', 'utf8', function(err, data){ // if (err) { // console.log(err) // } else { console.log(data) // } })} catch(e) { console.log(e)} 复制代码
尝试读取一个不存在的文件时.外层的try/catch 无法捕获这一异常.将输出 undefine. callback异步操作,异步调用的本体和callback属于不同的事件循环.而try/catch 只能捕获当前事件的异常.因此将无法捕获
Event (发布/订阅模式)
采用事件驱动模式.任务的执行不取决于代码的顺序,由某个事件来决定
var EventEmitter = require('events') var fs = require('fs')var eve = new EventEmitter()// 监听read事件eve.on('read', function(value){ console.log(value)})// 执行一个异步读取fs.readFile('./template.txt', 'utf8', function (err, data) { if (err) { console.log(err) } else { // 拿到数据后 发送一个read事件.并传递数据 eve.emit('read', data) }})复制代码
Promise
在ES6中提供了promise对象.给外界提供了统一的API.可用同步操作的流程来表达异步操作.避免了callback 中的嵌套层数过深,不便维护的弊端.
var fs = require('fs')function read (path) { return new Promise(function(resolve, reject){ fs.readFile(path, 'utf8', function(err, data){ if (err) { reject(err) } else { console.log(data) resolve() } }) })}read('./1.txt').then(function(){ return read('./2.txt')}).then(function(){ return read('./3.txt')}).then(function(){ console.log('执行结束')})复制代码
yield && co
通过Generator函数封装异步任务.在需要暂停的地方使用yield
var fs = require('fs')function *getData () { var a = yield readFile('./1.txt') console.log(a) var b = yield readFile('./2.txt') console.log(b) var c = yield readFile('./3.txt') console.log(c)}function readFile(path) { return new Promise(function(resolve, reject){ fs.readFile(path, 'utf8', function (err, data) { if (err) { reject(err) } else { resolve(data) } }) })}function co(gen) { var fn = gen() var lastVal return new Promise(function(resolve, reject){ !function next(lastVal) { var {value, done} = fn.next(lastVal) if (done) { resolve() } else { value.then(next, reject) } }() })}co(getData)复制代码
async/await
Async/Await应该是目前最简单的异步方案了
- async 表示这是一个async函数,await只能用在这个函数里面
- await 表示在这里等待promise返回结果了,再继续执行。
- await 后面跟着的应该是一个promise对象(其他返回值也可以,只是会立即执行)
- 捕获错误
var fs = require('fs')async function getData () { try { var a = await readFile('file') } catch(e) { console.log(e) } var b = await readFile('./2.txt') console.log(b) var c = await readFile('./3.txt') console.log(c)}function readFile(path) { return new Promise(function(resolve, reject){ fs.readFile(path, 'utf8', function (err, data) { if (err) { reject(err) } else { resolve(data) } }) })}getData()复制代码
参考文章