JavaScript異步流程控制的前世此生

JavaScript

javascript在設計之初.爲了不資源管理複雜問題(多個線程同時操做dom,以哪一個爲準),所以被設計成爲了單線程語言.javascript

提及異步就不得不提回調, 爲了解決多重回調嵌套致使代碼難以維護問題.javascript一直都在完善這個解決方案.html

在10多年中javascript 異步流程控制通過了java

callback -> event -> promise -> yield & co -> async await編程

Callback

ES6以前異步編程最經常使用的方法,若是回調函數嵌套層數較深,代碼將變得難以維護.而且在回調函數以外沒法捕獲回調函數中的異常.promise

var fs = require('fs')

try {
  fs.readFile('file', 'utf8', function(err, data){
    // if (err) {
    // console.log(err)
    // } else {
      console.log(data)
    // }
  })
} catch(e) {
  console.log(e)
}  
複製代碼

嘗試讀取一個不存在的文件時.外層的try/catch 沒法捕獲這一異常.將輸出 undefine. callback異步操做,異步調用的本體和callback屬於不一樣的事件循環.而try/catch 只能捕獲當前事件的異常.所以將沒法捕獲dom

Event (發佈/訂閱模式)

採用事件驅動模式.任務的執行不取決於代碼的順序,由某個事件來決定異步

var EventEmitter = require('events') 
var fs = require('fs')

var eve = new EventEmitter()

// 監聽read事件
eve.on('read', function(value){
  console.log(value)
})
// 執行一個異步讀取
fs.readFile('./template.txt', 'utf8', function (err, data) {
  if (err) {
    console.log(err)
  } else {
    // 拿到數據後 發送一個read事件.並傳遞數據 
    eve.emit('read', data)
  }
})

複製代碼

Promise

在ES6中提供了promise對象.給外界提供了統一的API.可用同步操做的流程來表達異步操做.避免了callback 中的嵌套層數過深,不便維護的弊端.async

var fs = require('fs')

function read (path) {
  return new Promise(function(resolve, reject){
    fs.readFile(path, 'utf8', function(err, data){
      if (err) {
        reject(err)
      } else {
        console.log(data)
        resolve()
      }
    })
  })
}

read('./1.txt').then(function(){
  return read('./2.txt')
}).then(function(){
  return read('./3.txt')
}).then(function(){
  console.log('執行結束')
})
複製代碼

yield && co

經過Generator函數封裝異步任務.在須要暫停的地方使用yield異步編程

var fs = require('fs')

function *getData () {
 var a = yield readFile('./1.txt')
 console.log(a)
 var b =  yield readFile('./2.txt')
 console.log(b)
 var c =  yield readFile('./3.txt')
 console.log(c)
}

function readFile(path) {
  return new Promise(function(resolve, reject){
    fs.readFile(path, 'utf8', function (err, data) {
      if (err) {
        reject(err)
      } else {
        resolve(data)
      }
    })
  })
}

function co(gen) {
  var fn = gen()
  var lastVal
  return new Promise(function(resolve, reject){
    !function next(lastVal) {
      var {value, done}  = fn.next(lastVal)
      if (done) {
        resolve()
      } else {
        value.then(next, reject)
      }
    }()
  })
}

co(getData)
複製代碼

async/await

Async/Await應該是目前最簡單的異步方案了函數

  • async 表示這是一個async函數,await只能用在這個函數裏面
  • await 表示在這裏等待promise返回結果了,再繼續執行。
  • await 後面跟着的應該是一個promise對象(其餘返回值也能夠,只是會當即執行)
  • 捕獲錯誤
var fs = require('fs')

async function getData () {
  try {
    var a = await readFile('file')
  } catch(e) {
    console.log(e)
  }  
  var b =  await readFile('./2.txt')
  console.log(b)
  var c =  await readFile('./3.txt')
  console.log(c)
}

function readFile(path) {
  return new Promise(function(resolve, reject){
    fs.readFile(path, 'utf8', function (err, data) {
      if (err) {
        reject(err)
      } else {
        resolve(data)
      }
    })
  })
}

getData()
複製代碼

參考文章 Generator 函數的含義與用法

相關文章
相關標籤/搜索