这篇文章主要是想回顾一下 CallBack 的一些使用方式node
所谓"异步",简单说就是一个任务分红几段,先执行第一段,而后转而执行其余任务,等作好了准备,再回过头执行第二段。这种不连续的执行,就叫异步。数组
例子中异步代码主要使用 node 读取文件,复习一下 fs 模块的 fs.readFile 方法异步
const fs = require('fs')
fs.readFile('./a.txt', 'utf8', function(err, data) {
if (err) return
console.log(data)
})
复制代码
例子中 readFile 第三个参数接受一个方法,这个方法会等待文件异步读取完毕后执行并传入结果。函数
主要有两个问题,错误处理、回调地狱。接下来用代码演示ui
try catch 只能捕获同步错误,对异步无能为力,好比this
try {
fs.readFile('./a.txt', 'utf8', function(err, data) {
throw new Error('报错了!')
})
} catch (err) {
console.log('捕获错误:', err)
}
复制代码
由于 fs.readFile
是异步执行的,因此 fs.readFile 外的 try catch
不能捕获内部错误。接下来修改代码捕获错误spa
fs.readFile('./a.txt', 'utf8', function(err, data) {
try {
throw new Error('报错了!')
} catch (err) {
console.log('捕获错误:', err)
}
})
复制代码
只须要把 try catch
放到内部就能捕获错误code
好比有个需求,要求先读取a.txt
的值,再读取b.txt
,再读取c.txt
等等等,这代码怎么写对象
fs.readFile('./a.txt', 'utf8', function(err, data) {
fs.readFile('./b.txt', 'utf8', function(err, data) {
fs.readFile('./c.txt', 'utf8', function(err, data) {
// ....
})
})
})
复制代码
若是再加上try catch
...同步
fs.readFile('./a.txt', 'utf8', function(err, data) {
try {
fs.readFile('./b.txt', 'utf8', function(err, data) {
try {
fs.readFile('./c.txt', 'utf8', function(err, data) {
try {
// ....
} catch (err) {
console.log('捕获错误:', err)
}
})
} catch (err) {
console.log('捕获错误:', err)
}
})
} catch (err) {
console.log('捕获错误:', err)
}
})
复制代码
这代码就看着想吐了。
如今有个需求,看下面代码,我想在 a.txt
、b.txt
、c.txt
都读取完毕后返回 collect
对象,这该怎么实现
const collect = {}
fs.readFile('./a.txt', 'utf8', function(err, data) {
collect.a = data
})
fs.readFile('./b.txt', 'utf8', function(err, data) {
collect.b = data
})
fs.readFile('./c.txt', 'utf8', function(err, data) {
collect.c = data
})
复制代码
思考:能不能写一个方法,每次在 fs.readFile 执行完后查看 collect
key 的数量
const collect = {}
// 哨兵函数
function out() {
if (Object.keys(collect).length === 3) {
console.log(collect)
}
}
fs.readFile('./a.txt', 'utf8', function(err, data) {
collect.a = data
out()
})
fs.readFile('./b.txt', 'utf8', function(err, data) {
collect.b = data
out()
})
fs.readFile('./c.txt', 'utf8', function(err, data) {
collect.c = data
out()
})
复制代码
这个 out
方法就叫哨兵函数,可是这个方法用起来仍是不方便。我想写一个 after 方法,接受次数和回调就能实现 out 方法好比
let out = after(3, function(data){
console.log(data)
})
复制代码
接下来实现这个 after 方法
function after(times, callback) {
return function() {
if (--times === 0) {
callback()
}
}
}
let out = after(3, function(data){
console.log(data)
})
// 执行 out 3次
out()
out()
out() // console.log 执行
复制代码
用 after()
生成的 out
代替以前的 out
感受舒服了好多。可是仍是不够,若是我想在异步都执行完毕后执行多个方法怎么办
思考:用数组保存要执行都方法,当须要执行的时候依次执行
实现一个 Dep 对象
,Dep 上有一个保存CallBack
的 arr
数组、一个把CallBack
添加到arr
的on
方法,一个哨兵函数emit
,实现和用法以下
const collect = {}
const Dep = {
arr: [],
on(fn) {
this.arr.push(fn)
},
emit() {
if (Object.keys(collect).length === 3) {
this.arr.forEach(function(fn){
fn()
})
}
}
}
Dep.on(function() {
console.log(1)
})
Dep.on(function() {
console.log(2)
})
fs.readFile('./a.txt', 'utf8', function(err, data) {
collect.a = data
Dep.emit()
})
fs.readFile('./b.txt', 'utf8', function(err, data) {
collect.b = data
Dep.emit()
})
fs.readFile('./c.txt', 'utf8', function(err, data) {
collect.c = data
Dep.emit()
})
复制代码
这就是特别简单的发布订阅,只是扩展一下思路