寫在前面, 這個話題其實還挺大的, 我自己恐怕力有不逮, 所以只能算是筆記總結, 寫的肯定會有點簡略. 有錯誤實在太正常了. 希望能多多指教.
這篇文章僅僅是解釋一下現有的非同步程式設計方案不涉及具體原理, 但是我的想法是試試看能不能每個方案都自己實現一遍, 所以可能是系列文章也可能就此太監了.
回撥函式
想必大家都看過上面的圖片, 雖然不是js程式碼, 甚至不是回撥, 但是各位同學估計對callback hell感同身受. 但是事實上回撥函式跟非同步程式設計並沒有必然的聯絡. 回撥只是一種設計模式. 同步程式碼同樣可以使用回撥只是大部分時候會讓人覺得多此一舉而已.
大家都知道回撥並不是那麼優雅, 很多時候程式碼可能寫成這樣:
asyncJob1(function () {
asyncJob2(function () {
asyncJob3(function () {
// your code
})
})
})
複製程式碼
雖然問題算是解決了吧, 但是程式碼看著真是令人頭痛. 玩意邏輯稍微複雜一點說不定真會出現下面的情況:
大概美軍也是回撥寫多了吧2333.
事件驅動
大部分gui程式都是採用事件驅動, web當然也不例外, 最典型的就是dom事件:
div.on('click', function () {
// balabala
})
複製程式碼
這個幾乎都是跟回撥和事件迴圈相關的, 後面會詳細講到的.
Promise
根據promise/A+的規範:
一個Promise必須處在其中之一的狀態:pending, fulfilled 或 rejected. 如果是pending狀態,則promise可以轉換到fulfilled或rejected狀態。 如果是fulfilled狀態,則promise不能轉換成任何其它狀態。 如果是rejected狀態,則promise不能轉換成任何其它狀態。
Promise都會有個then方法, 制定了fulfilled和rejected兩種請況的回撥, 同時then會返回一個Promise物件, 這就允許我們鏈式呼叫了:
ajax('xxx').then(res => res).then(res => res)
複製程式碼
雖然Promise的方案比回撥已經好太多了. 但是不難發現多個then其實未必比回撥好看太多, 只能說把回撥鋪平了. 可能Promise並不是最優雅的解決方案.
generator
generator在各個語言裡都有出現, python,c#等, 這裡不說概念, 只說在js裡該怎麼使用就行, 基本理解就是generator是擁有多個返回值的函式, 每次呼叫next就會呼叫一次返回. 這就是所謂 控制權移交, 當然這樣不足以體現其優越性, generator最重要的特性就是分步資料傳遞: next的引數可以作為上次yield的返回值, 其實直覺上不是很能理解, 不過我們這裡只要知道有這麼個東西就成, 下面是程式碼示例:
var f = function* (x) {
var y = yield x + 1
yield y + 1
return y
}
var t = f(1)
var s = t.next()
console.log(s) // 2
var p = t.next(4) // 這裡的4作為上次yield的返回值
console.log(p) // 5
複製程式碼
有這麼一個特性, 那麼下面的操作性就很強了, 比如tj的著名的co:
co(function* () {
var result = yield Promise.resolve(true);
return result;
}).then(function (value) {
console.log(value);
}, function (err) {
console.error(err.stack);
});
複製程式碼
可以說寫法已經很同步了. 當然這個也不是最終的解決方案, 不然為什麼koa2要用async/await呢hhh
async/await
同樣對於async/await 我們先不糾結其原理, 我們只需要知道它是generator和promise二者結合起來的語法糖(不過也真夠甜的)
每個async必然返回一個Promise物件, 所以async會像瘟疫一個把你的每個函式都變成async函式, 所謂可以async的終將async, 下面是例子:
// Promise的解決方案
function foo () {
return ajax('xxx').then(res => res).then(res => res)
}
async function bar () {
try {
const response = await ajax('xxx')
console.log(response)
} catch (e) {
throw e
}
}
複製程式碼
是不是優雅了太多, 除了一大片async/await, 不過也算小小的代價.
利用午休時間碼了出來, 不知道有多少錯誤呢hhh