前言
上黨網站建設公司創新互聯公司,上黨網站設計制作,有大型網站制作公司豐富經驗。已為上黨1000多家提供企業網站建設服務。企業網站搭建\成都外貿網站建設要多少錢,請找那個售后服務好的上黨做網站的公司定做!
異步編程從早期的 callback、事件發布\訂閱模式到 ES6 的 Promise、Generator 在到 ES2017 中 async,看似風格迥異,但是還是有一條暗線將它們串聯在一起的,就是希望將異步編程的代碼表達盡量地貼合自然語言的線性思維。
以這條暗線將上述幾種解決方案連在一起,就可以更好地理解異步編程的原理、魅力。
├── 事件發布\訂閱模式 <= Callback
├── Promise <= 事件發布\訂閱模式
├── Async、Await <= Promise、Generator
事件發布\訂閱模式 <= Callback
這個模式本質上就是回調函數的事件化。它本身并無同步、異步調用的問題,我們只是使用它來實現事件與回調函數之間的關聯。比較典型的有 NodeJS 的 events 模塊
const { EventEmitter } = require('events') const eventEmitter = new EventEmitter() // 訂閱 eventEmitter.on("event", function(msg) { console.log("event", msg) }) // 發布 eventEmitter.emit("event", "Hello world")
那么這種模式是如何與 Callback 關聯的呢?我們可以利用 Javascript 簡單實現 EventEmitter,答案就顯而易見了。
class usrEventEmitter { constructor () { this.listeners = {} } // 訂閱,callback 為每個 event 的偵聽器 on(eventName, callback) { if (!this.listeners[eventName]) this.listeners[eventName] = [] this.listeners[eventName].push(callback) } // 發布 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) } }
上述實現忽略了很多細節,例如異常處理、多參數傳遞等。只是為了展示事件訂閱\發布模式。
很明顯的看出,我們使用這種設計模式對異步編程做了邏輯上的分離,將其語義化為
// 一些事件可能會被觸發 eventEmitter.on // 當它發生的時候,要這樣處理 eventEmitter.emit
也就是說,我們將最初的 Callback 變成了事件監聽器,從而優雅地解決異步編程。
Promise <= 事件發布\訂閱模式
使用事件發布\訂閱模式時,需要我們事先嚴謹地設置目標,也就是上面所說的,必須要縝密地設定好有哪些事件會發生。這與我們語言的線性思維很違和。那么有沒有一種方式可以解決這個問題,社區產出了 Promise。
const promise = new Promise(function(resolve, reject) {
try {
setTimeout(() => {
resolve('hello world')
}, 500)
} catch (error) {
reject(error)
}
})
// 語義就變為先發生一些異步行為,then 我們應該這么處理 promise.then(msg => console.log(msg)).catch(error => console.log('err', error))
那么這種 Promise 與事件發布\訂閱模式有什么聯系呢?我們可以利用 EventEmitter 來實現 Promise,這樣可能會對你有所啟發。
我們可以將 Promise 視為一個 EventEmitter,它包含了 { state: 'pending' } 來描述當前的狀態,同時偵聽它的變化
具體實現如下
const { EventEmitter } = require('events') class usrPromise extends EventEmitter { // 構造時候執行 constructor(executor) { super() // 發布 const resolve = (value) => this.emit('resolve', value) const reject = (reason) => this.emit('reject', reason) if (executor) { // 模擬 event loop,注此處利用 Macrotask 來模擬 Microtask setTimeout(() => executor(resolve, reject)) } } then(resolveHandler, rejectHandler) { const nextPromise = new usrPromise() // 訂閱 resolve 事件 if (resolveHandler) { const resolve = (data) => { const result = resolveHandler(data) nextPromise.emit('resolve', result) } this.on('resolve', resolve) } // 訂閱 reject 事件 if (rejectHandler) { const reject = (data) => { const result = rejectHandler(data) nextPromise.emit('reject', result) } this.on('reject', reject) } else { this.on('reject', (data) => { promise.emit('reject', data) }) } return nextPromise } catch(handler) { this.on('reject', handler) } }
我們使用 then 方法來將預先需要定義的事件偵聽器存放起來,同時在 executor 中設定這些事件該在什么時候實行。
可以看出從事件發布\訂閱模式到 Promise,帶來了語義上的巨大變革,但是還是需要使用 new Promise 來描述整個狀態的轉換,那么有沒有更好地實現方式呢?
async、await <= Promise、Generator
async、await 標準是 ES 2017 引入,提供一種更加簡潔的異步解決方案。
async function say(greeting) { return new Promise(function(resolve, then) { setTimeout(function() { resolve(greeting) }, 1500) }) } ;(async function() { let v1 = await say('Hello') console.log(v1) let v2 = await say('World') console.log(v2) })()
await 可以理解為暫停當前 async function 的執行,等待 Promise 處理完成。。若 Promise 正常處理(fulfilled),其回調的resolve函數參數作為 await 表達式的值。
async、await 的出現,減少了多個 then 的鏈式調用形式的代碼。下面我們結合 Promise 與 Generator 來實現 async、await
function async(makeGenerator) { return function() { const generator = makeGenerator.apply(this, arguments) function handle({ value, done }) { if (done === true) return Promise.resolve(value) return Promise.resolve(value).then( (res) => { return handle(generator.next(res)) }, function(err) { return handle(generator.throw(err)) } ) } try { return handle(generator.next()) } catch (ex) { return Promise.reject(ex) } } } async(function*() { var v1 = yield say('hello') console.log(1, v1) var v2 = yield say('world') console.log(2, v2) })()
本質上就是利用遞歸完成 function* () { ... } 的自動執行。相比與 Generator 函數,這種形式無需手動執行,并且具有更好的語義。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持創新互聯。文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,
網站欄目:新手如何快速理解js異步編程
文章出自:http://vcdvsql.cn/article20/ggheco.html
成都網站建設公司_創新互聯,為您提供自適應網站、關鍵詞優化、定制開發、網站收錄、移動網站建設、網站內鏈
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯