前言
異步編程從早期的 callback、事件發(fā)布/訂閱模式到 ES6 的 Promise、Generator 在到 ES2017 中 async,看似風(fēng)格迥異,但是還是有一條暗線將它們串聯(lián)在一起的,就是希望將異步編程的代碼表達(dá)盡量地貼合自然語(yǔ)言的線性思維。
以這條暗線將上述幾種解決方案連在一起,就可以更好地理解異步編程的原理、魅力。
├── 事件發(fā)布/訂閱模式 <= Callback
├── Promise <= 事件發(fā)布/訂閱模式
├── Async、Await <= Promise、Generator
事件發(fā)布/訂閱模式 <= Callback
這個(gè)模式本質(zhì)上就是回調(diào)函數(shù)的事件化。它本身并無(wú)同步、異步調(diào)用的問(wèn)題,我們只是使用它來(lái)實(shí)現(xiàn)事件與回調(diào)函數(shù)之間的關(guān)聯(lián)。比較典型的有 NodeJS 的 events 模塊
const { EventEmitter } = require('events')const eventEmitter = new EventEmitter()// 訂閱eventEmitter.on("event", function(msg) {console.log("event", msg)})// 發(fā)布eventEmitter.emit("event", "Hello world")那么這種模式是如何與 Callback 關(guān)聯(lián)的呢?我們可以利用 Javascript 簡(jiǎn)單實(shí)現(xiàn) EventEmitter,答案就顯而易見(jiàn)了。
class usrEventEmitter {constructor () {this.listeners = {}}// 訂閱,callback 為每個(gè) event 的偵聽器on(eventName, callback) {if (!this.listeners[eventName]) this.listeners[eventName] = []this.listeners[eventName].push(callback)}// 發(fā)布emit(eventName, params) {this.listeners[eventName].forEach(callback => {callback(params)})}// 注銷off(eventName, callback) {const rest = this.listeners[eventName].fitler(elem => elem !== callback)this.listeners[eventName] = rest}// 訂閱一次once(eventName, callback) { const handler = function() {callback()this.off(eventName, handler)}this.on(eventName, handler)}}上述實(shí)現(xiàn)忽略了很多細(xì)節(jié),例如異常處理、多參數(shù)傳遞等。只是為了展示事件訂閱/發(fā)布模式。
很明顯的看出,我們使用這種設(shè)計(jì)模式對(duì)異步編程做了邏輯上的分離,將其語(yǔ)義化為
// 一些事件可能會(huì)被觸發(fā)eventEmitter.on// 當(dāng)它發(fā)生的時(shí)候,要這樣處理eventEmitter.emit
也就是說(shuō),我們將最初的 Callback 變成了事件監(jiān)聽器,從而優(yōu)雅地解決異步編程。
Promise <= 事件發(fā)布/訂閱模式
使用事件發(fā)布/訂閱模式時(shí),需要我們事先嚴(yán)謹(jǐn)?shù)卦O(shè)置目標(biāo),也就是上面所說(shuō)的,必須要縝密地設(shè)定好有哪些事件會(huì)發(fā)生。這與我們語(yǔ)言的線性思維很違和。那么有沒(méi)有一種方式可以解決這個(gè)問(wèn)題,社區(qū)產(chǎn)出了 Promise。
const promise = new Promise(function(resolve, reject) {
try {
setTimeout(() => {
resolve('hello world')
}, 500)
} catch (error) {
reject(error)
}
})
// 語(yǔ)義就變?yōu)橄劝l(fā)生一些異步行為,then 我們應(yīng)該這么處理promise.then(msg => console.log(msg)).catch(error => console.log('err', error))
新聞熱點(diǎn)
疑難解答
圖片精選