一杯咖啡☕️的時(shí)間,聊聊 js 異步解決方案
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
回調(diào)函數(shù)(callback)回調(diào)函數(shù) 簡單理解就是一個(gè)函數(shù)被作為參數(shù)傳遞給另一個(gè)函數(shù)?;卣{(diào)是早期最常用的一種異步解決方案。 回調(diào)并不一定就是異步,并沒有直接關(guān)系。 舉個(gè)簡單的例子: function f1(cb) { setTimeout(() => { cb && cb(); }, 2000); } f1(() => { console.log("1"); }); 如上,我們使用 采用這種方式,我們把同步操作變成了異步操作, 回調(diào)優(yōu)缺點(diǎn)優(yōu)點(diǎn):簡單、容易理解 缺點(diǎn):代碼不優(yōu)雅,可讀性差,不易維護(hù),高度耦合,層層嵌套造成回調(diào)地獄 事件監(jiān)聽(發(fā)布訂閱模式)發(fā)布訂閱模式 定義了對象間的一種一對多的依賴關(guān)系,當(dāng)一個(gè)對象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對象都將會(huì)得到通知。 其實(shí)我們都用過發(fā)布訂閱模式,比如我們在 document.body.addEventListener('click', function () { console.log('點(diǎn)擊'); }) 但這只是對發(fā)布訂閱模式最簡單的使用,在很多場景下我們經(jīng)常會(huì)使用一些自定義事件來滿足我們的需求。 發(fā)布訂閱模式有很多種實(shí)現(xiàn)方式,下面我們用 class Emitter { constructor() { // _listener數(shù)組,key為自定義事件名,value為執(zhí)行回調(diào)數(shù)組-因?yàn)榭赡苡卸鄠€(gè) this._listener = [] } // 訂閱 監(jiān)聽事件 on(type, fn) { // 判斷_listener數(shù)組中是否存在該事件命 // 存在將回調(diào)push到事件名對應(yīng)的value數(shù)組中,不存在直接新增 this._listener[type] ? this._listener[type].push(fn) : (this._listener[type] = [fn]) } // 發(fā)布 觸發(fā)事件 trigger(type, ...rest) { // 判斷該觸發(fā)事件是否存在 if (!this._listener[type]) return // 遍歷執(zhí)行該事件回調(diào)數(shù)組并傳遞參數(shù) this._listener[type].forEach(callback => callback(...rest)) } } 如上所示,我們創(chuàng)建了一個(gè) // 創(chuàng)建一個(gè)emitter實(shí)例 const emitter = new Emitter() emitter.on("done", function(arg1, arg2) { console.log(arg1, arg2) }) emitter.on("done", function(arg1, arg2) { console.log(arg2, arg1) }) function fn1() { console.log('我是主程序') setTimeout(() => { emitter.trigger("done", "異步參數(shù)一", "異步參數(shù)二") }, 1000) } fn1() 我們先創(chuàng)建一個(gè) 事件監(jiān)聽優(yōu)缺點(diǎn)優(yōu)點(diǎn):比較符合模塊化思想,我們自寫監(jiān)聽器時(shí)可以做很多優(yōu)化從而更好地監(jiān)控程序運(yùn)行。 缺點(diǎn):整個(gè)程序變成了事件驅(qū)動(dòng),流程上或多或少都會(huì)有點(diǎn)影響,每次使用還得注冊事件監(jiān)聽再進(jìn)行觸發(fā)挺麻煩的,代碼也不太優(yōu)雅。 PromiseES2015(ES6)標(biāo)準(zhǔn)化和引入了 簡單來說就是用同步的方式寫異步的代碼,可用來解決回調(diào)地獄問題。
我們用 function analogAsync(n) { return new Promise((resolve) => { setTimeout(() => resolve(n + 500), n); }); } function fn1(n) { console.log(`step1 with ${n}`); return analogAsync(n); } function fn2(n) { console.log(`step2 with ${n}`); return analogAsync(n); } function fn3(n) { console.log(`step3 with ${n}`); return analogAsync(n); } 用 function fn() { let time1 = 0; fn1(time1) .then((time2) => fn2(time2)) .then((time3) => fn3(time3)) .then((res) => { console.log(`result is ${res}`); }); } fn(); Promise 優(yōu)缺點(diǎn)優(yōu)點(diǎn): 缺點(diǎn): Generator
示例: function *generatorFn() { console.log("a"); yield '1'; console.log("b"); yield '2'; console.log("c"); return '3'; } let it = generatorFn(); it.next(); it.next(); it.next(); it.next(); 上面這個(gè)示例就是一個(gè)
Generator 優(yōu)缺點(diǎn)優(yōu)點(diǎn):優(yōu)雅的流程控制方式,可以讓函數(shù)可中斷執(zhí)行 缺點(diǎn): async/awaitES2017 標(biāo)準(zhǔn)引入了 async 在做什么
await 在等待什么
上述用 async function fn() { let time1 = 0; let time2 = await fn1(time1); let time3 = await fn2(time2); let res = await fn3(time3); console.log(`result is ${res}`); } fn(); 輸出結(jié)果和上面用 async/await 優(yōu)缺點(diǎn)優(yōu)點(diǎn):內(nèi)置執(zhí)行器,更好的語義,更廣的適用性 缺點(diǎn):濫用 該文章在 2023/7/29 9:42:29 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |