javascript在設計之初.為了避免資源管理複雜問題(多個執行緒同時操作dom,以哪個為準),因此被設計成為了單執行緒語言.
說起非同步就不得不提回撥, 為了解決多重回撥巢狀導致程式碼難以維護問題.javascript一直都在完善這個解決方案.
在10多年中javascript 非同步流程控制經過了
callback -> event -> promise -> yield & co -> async await
Callback
ES6之前非同步程式設計最常用的方法,如果回撥函式巢狀層數較深,程式碼將變得難以維護.並且在回撥函式之外無法捕獲回撥函式中的異常.
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 只能捕獲當前事件的異常.因此將無法捕獲
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 中的巢狀層數過深,不便維護的弊端.
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 函式的含義與用法