前言
这篇文章主要是想回顾一下 CallBack 的一些使用方式
异步
所谓"异步",简单说就是一个任务分成几段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。这种不连续的执行,就叫异步。
前置知识
例子中异步代码主要使用 node 读取文件,复习一下 fs 模块的 fs.readFile 方法
const fs = require('fs')
fs.readFile('./a.txt', 'utf8', function(err, data) {
if (err) return
console.log(data)
})
复制代码例子中 readFile 第三个参数接受一个方法,这个方法会等待文件异步读取完毕后执行并传入结果。
回调的问题
主要有两个问题,错误处理、回调地狱。接下来用代码演示
错误处理
try catch 只能捕获同步错误,对异步无能为力,比如
try {
fs.readFile('./a.txt', 'utf8', function(err, data) {
throw new Error('报错了!')
})
} catch (err) {
console.log('捕获错误:', err)
}
复制代码因为 fs.readFile 是异步执行的,所以 fs.readFile 外的 try catch 不能捕获内部错误。接下来修改代码捕获错误
fs.readFile('./a.txt', 'utf8', function(err, data) {
try {
throw new Error('报错了!')
} catch (err) {
console.log('捕获错误:', err)
}
})
复制代码只需要把 try catch 放到内部就能捕获错误
回调地狱
比如有个需求,要求先读取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()
})
复制代码这就是特别简单的发布订阅,只是扩展一下思路
本文深入探讨异步编程中回调的使用方式,包括其在Node.js中读取文件的应用,以及如何解决回调地狱和错误处理难题。
586

被折叠的 条评论
为什么被折叠?



