什么是异步?html
所谓异步,简单来讲就是异步任务(不会立刻就完成的任务);可是js不会等待你这个任务完成,而是直接执行下边的任务;等到你上边的任务完成以后才会去执行相应的逻辑。好比js读取文件就是异步的过程。node
一、回调函数git
场景: 读取一个文件github
let fs = require('fs')
fs.readFile('./1.txt', 'utf8', function(err, data){
// 回调的特色是第一个参数通常为错误对象
if (err) { // 若是err有值说明程序出错了
console.log(err)
} else { // 不然表示成功获取到数据data
console.log(data)
}
})
复制代码
固然回调函数也有它的缺点:编程
funnction readFile (fileName) {
fs.readFile(fileName, 'utf8', function (data) {
if (err) {
console.log(err)
} else {
console.log(data)
}
})
}
try {
readFile('./1.txt')
} catch (e) { // 若是上边读取文件出错,获取不到错误信息
console.log('err', e)
}
复制代码
// readFile 方法中没法返回读取到文件的内容(data)
复制代码
fs.readFile('./template.txt', 'utf8', function (err, template) {
fs.readFile('./data.txt', 'utf8', function (err, data) {
console.log(template, data)
})
})
// 这样的代码称为恶魔金字塔;且有如下问题
// 一、代码很是难看
// 二、难以维护
// 三、效率比较低,由于它们是串行的;一次只能请求一个文件
复制代码
二、事件发布订阅(为了解决回调嵌套的问题)promise
let EventEmitter = require('events')
// nodejs核心模块之一;包含两个核心方法 on >> 表示注册监听 emit >> 表示发射事件
let eve = new EventEmitter()
let html = {} // 存放页面模板和数据
eve.on('onloading', function (key, value) {
html[key] = value
if (Object.keys(html).length == 2) {
console.log(html)
}
})
fs.readFile('./template.txt', 'utf8', function (err, template) {
eve.emit('onloading', template) // 触发onloading事件,执行事件的回调函数向html中填入模板
})
fs.readFile('./data.txt', 'utf8', function (err, template) {
eve.emit('onloading', template) // 触发onloading事件,执行事件的回调函数向html中填入数据
})
复制代码
三、哨兵变量 (一样处理回调嵌套的问题) 事件发布订阅已经能够解决回调嵌套的问题,可是还须要引入events模块; 利用哨兵变量同样能够解决回调嵌套的问题,且不须要引入其余模块bash
// 定义一个哨兵函数来处理
function done (key, value) {
html[key] = value
if (Object.keys(html).length == 2) {
console.log(html)
}
}
fs.readFile('./template.txt', 'utf8', function (err, template) {
done('template', template)
})
fs.readFile('./data.txt', 'utf8', function (err, template) {
done('data', data)
})
// 能够封装一个高阶函数去生成哨兵函数
function render (length, cb) {
let htm = {}
return function (key, value) {
html[key] = value
if (Object.keys(html).length == length) {
cb(html)
}
}
}
let done = render(2, function (html) {
console.log(html)
})
复制代码
四、Promise 上述方法都是用回调函数来处理异步;咱们的目标是把异步往同步的方向靠拢服务器
let promise1 = new Promise(function (resolve, reject) {
fs.readFile('./1.txt', 'utf8', function (err, data) {
resolve(data)
})
})
promise1
.then(function (data) {
console.log(data)
})
复制代码
五、生成器Generator 当咱们在调用一个函数的时候,它并不会立刻执行,而是须要咱们手动的去执行迭代操做(next方法);简单来讲,调用生成器函数会返回一个迭代器,能够用迭代器来执行遍历每一个中断点(yield) 调用next方法会有返回值value,是生成器函数对外输出的数据;next方法还能够接受参数,是向生成器函数内部输入的数据async
// 方法名前边加*就是生成器函数
function *foo () {
var index = 0;
while (index < 2) {
yield index++; //暂停函数执行,并执行yield后的操做
}
}
var bar = foo(); // 返回的实际上是一个迭代器
console.log(bar.next()); // { value: 0, done: false }
console.log(bar.next()); // { value: 1, done: false }
console.log(bar.next()); // { value: undefined, done: true }
复制代码
function readFile (filaName) {
return new Promise(function (resolve, reject) {
fs.readFile(filename, function (err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
})
}
function *read() {
let template = yield readFile('./template.txt')
let data = yield readFile('./data.txt')
return {
template: template,
data: data
}
}
// 生成迭代器r1
let r1 = read()
let templatePromise = r1.next().value
templatePromise.then(function(template) {
// 将获取到的template的内容传递给生成器函数
let dataPromsie = r1.next(template).value
dataPromise.then(function(data) {
//最后一次执行next传入data的值;最后返回{template, data}
let result = r1.next(data).value
console.log(result)
})
})
复制代码
生成器 + promise的实现已经有了一些同步的样子; 借助一些工具(co),能够优雅的编写上述的代码
//实现 co 方法
// 参数是一个生成器函数
function co (genFn) {
let r1 = genFn()
return new Promise(function(resolev, reject) {
!function next(lastVal) {
let p1 = r1.next(lastVal)
if (p1.done) {
resolve(p1.value)
} else {
p1.value.then(next, reject)
}
}()
})
}
如今获取上边的result能够这样来取
co(read).then(function(result) {
console.log(result)
})
复制代码
六、Async/await Async实际上是一个语法糖,它的实现就是将Generator函数和自动执行器(co),包装在一个函数中
async function read() {
let template = await readFile('./template.txt');
let data = await readFile('./data.txt');
return template + '+' + data;
}
// 等同于
function read(){
return co(function*() {
let template = yield readFile('./template.txt');
let data = yield readFile('./data.txt');
return template + '+' + data;
});
}
复制代码
结论:异步编程发展的目标就是让异步逻辑的代码看起来像同步同样;发展到Async/await;是处理异步编程探索的一个里程碑。