這篇文章主要是想回顧一下 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()
})
複製代碼
這就是特別簡單的發佈訂閱,只是擴展一下思路