要解决的问题
任务: 在取到文件名为finename1的文件之后, 在取filename2, 然后在取filename3
传统的回调嵌套:
fs.readFile(filename1, function (err, data) {
if(!err){
rs.readFile(filename2, function (err, data) {
if(!err){
rs.readFile(filename3, function (err, data)...
}else{
}
}
}else{
...
}
})
目标: 将异步代码以同步的形式表达
data1 = 异步请求1
// 用data1做一些事情
data2 = 异步请求2
// 用data2做一些事情
data3 = 异步请求3
// 用data3做一些事情
方案就是: Promise + Generator = Async
Promise
Promise是异步回调嵌套的解决方案之一, Promise能看作一个异步流程机, 数个Promise组成一个完成流程, Promise提供相应的接口实现对接
es6的Promise实现遵循Promise A+ 规范 Promise A+ Promise A+规范的翻译
Promise的异步编程能力
利用Promise封装fs.readFile
Promise的表达
// 封装fs.readFile方法
const fs = require('fs')
const readFile = filename => new Promise((resolve, reject) => {
fs.readFile(filename, (err, data) => {
if (err) {
reject(err)
} else {
resolve(data.toString())
}
})
})
// 利用封装的Promise去解决任务
readFile(filename1)
.then(v => {
return readFile(filename2)
})
.then(v => {
return readFile(filename3)
})
...
缺点
Promise封装异步操作有几个缺点
- 可读性比传统的好, 不够完美, 会有Promise自身的沉冗代码
- Promise一旦创立立即执行函数, 可能需要外面在封装一层函数实现延迟执行, 不够灵活
generate函数
Generator 函数是 ES6, 提供的一种异步编程解决方案,语法行为和运行机制与传统函数完全不同。
- 传统函数 只能调用并且传入参数
function foo () { // 创建
//...dosomething
}
foo(arg) // 调用函数并且传入参数
创建函数 --> 调用函数
- generate函数 generate函数在函数体外有完全的流程控制权, 每一个yield关键字都相当于一个流程的断点
const fn1 = function () {//...}
const fn2 = function () {//...}
function* foo () { // 创建函数
const data1 = yield fn1()
const data2 = yield fn2()
}
const f = foo() // 创建一个迭代器, 函数里面代码不会运行
f.next() // 执行fn1()
f.next(arg) // 执行fn2()并且将值传入函数内, data1接收这个值
f.throw(new Error()) // 将错误传入函数内
f.return() // 将函数return掉
结合Promise
下面是generate结合Promise的异步流程封装, 先写一个手动执行器
function* read() {
const a = yield readFile('a.txt')
console.log(a)
const b = yield readFile('b.txt')
console.log(b)
}
const spwan = function (gen) {
const g = gen()
let step1 = g.next()
step1.value.then(v => {
let step2 = g.next(v)
step2.value.then(v => {
let step3 = g.next(v)
//...
})
})
}
spwan(read)
可以看出上面是一个递归
generate异步自动执行器
结合的具体思路其实就是在yield后面的Promise在追加一个回调函数, 这个回调函数调用generator g.next(v), 这样就能利用递归跑完所有的yield
function* read() {
const a = yield readFile('a.txt')
console.log(a)
const b = yield readFile('b.txt')
console.log(b)
}
const spwan = function (gen) {
const g = gen()
function next(data) {
const {value, done} = g.next(data)
if(done){ // 归
return
}
value.then(data => {// 递
next(data)
})
}
next()
}
spwan(read)
上述的自动执行器已经可以将read函数里面的异步流程全部执行了
异步流程已经以同步形式执行了出来, 可读行非常高
async
async是ES7的特性,是上述方法的语法糖
async function read() {
const a = await readFile('a.txt')
const b = await readFile('b.txt')
return a + b
}
read()
.then(v => {
console.log(v)
})
async在js引擎层面实现了一个执行器
上述代码read()相当于spwan(read)
await 相当于yield
总结
fs.readFile(filename1, function (err, data) {
if(!err){
rs.readFile(filename2, function (err, data) {
if(!err){
rs.readFile(filename3, function (err, data)...
}else{
}
}
}else{
...
}
})
↓ ↓ ↓
async function read() {
const a = await readFile('a.txt')
const b = await readFile('b.txt')
return a + b
}
支持
node7.6已经是默认支持async, ie想都别想 (手动滑稽, 绝大部分标准浏览器已经实现
水平有限, 欢迎指正哈