異步 CallBack 回顧

前言

這篇文章主要是想回顧一下 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.txtb.txtc.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 上有一個保存CallBackarr 數組、一個把CallBack添加到arron方法,一個哨兵函數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()
})
複製代碼

這就是特別簡單的發佈訂閱,只是擴展一下思路

相關文章
相關標籤/搜索