在理解js異步編程時, 咱們先再心中想一下爲何js語言會引入異步任務?異步到底解決了哪些問題?理解了這些以後,咱們才能更好地運行異步編程思想去書寫咱們的業務代碼邏輯。。。下面寫一下我的對異步模型的理解javascript
所謂js中的任務,通俗點咱們能夠理解爲等待運行的js代碼(這裏不搞那些專業術語),到此咱們能夠分爲順序當即執行的代碼(同步任務),以及非當即順序執行的代碼(異步任務)。html
同步任務有個特色,就是順序執行,代碼被編譯解析後按照既定的順序去一步一步執行, 這種執行方式效率高嗎?視狀況而定。 若是碰到一串耗時代碼,意味着此代碼段後面的代碼須要等待該代碼執行完畢他才能執行, 這當然是不行的(代碼運行被堵塞了); 因此此時便引入異步的概念,咱們把這段耗時任務扔給其餘執行器(或者說線程)去處理,咱們 只須要獲取其餘執行器處理後的結果,讓結果代碼滯後執行,或者說到相應的時機去執行(怎麼去判斷時機,發佈訂閱,先不說了) 讓主線程繼續執行其同步任務,這樣效率是否是提升了,至少不會發生代碼堵塞的問題了吧 :)
引入異步任務是爲了提升代碼執行的效率和速度,我以爲這只是結果的一部分。 爲何呢? 我的理解仍是js這門語言的缺陷,js做爲一種單線程語言,意味着它在處理 多任務併發時 沒有了多線程語言(如java)的優點, 一個主線程下代碼只得一行行執行咯;cpu的多核能力也不能徹底發揮啊,emm... 因此異步任務的引入 必定程度上也提高了js在處理多任務的能力吧。 其實吧,js中異步任務(如網絡請求,定時器, 事件監聽等)是瀏覽器的其餘進程/線程 爲js主線程分擔了處理多任務的壓力, 瀏覽器其餘進程/線程將異步任務處理後結果扔到js的事件循環機制的任務隊列裏,那麼這裏必然涉及到 進程/線程間的通訊,必定程度上也是會損耗部分效率的
宏觀上來講: 提高js代碼執行效率, 怎麼就提升了? 思考一下 提高js處理多任務的能力, 怎麼去提高? 思考一下
前面提到異步任務的概念:非當即執行的代碼, 固然是不徹底準確的啦,
js中的異步任務會被放到任務隊列(task queue)的任務,經過js事件循環機制(其實就是js主線程一直輪詢訪問任務隊列);
"任務隊列"會通知js主線程,當某個異步任務能夠執行了,該任務纔會進入主線程的執行棧執行;vue
因此異步任務的特色之一是存在一種等待狀態,滯後執行;那麼js怎麼來實現異步模式呢?java
執行棧 + 任務隊列
那麼js中到底哪些纔是異步任務呢? 有具體規範嗎?沒找到明確的規範我的理解:凡是被放到事件隊列裏的任務就是異步任務,這些任務與運行環境相關
而執行棧只是做爲任務的消費者而已,真正生產異步任務的生產者是:瀏覽器那些DOM API,網絡線程,計時器線程; node環境下的事件等。。。node
異步編程爲了啥?固然是爲了更快的執行代碼任務啊,怎麼作?那代碼爲何慢呢?任務太多了呀,因此咱們要對任務進行合理拆分git
f1(); // f1爲耗時任務 f2(); // f2依賴f1的結果 function f1(cb) { setTimeout(() => { // f1 的邏輯代碼 // 。。。 cb() }) } f1(f2) // f1被轉化爲異步任務,f2在它以後執行
回調的方式代碼耦合性太強,也不能捕獲異常try catchgithub
事件監聽的本質在於 事件狀態驅動,觸發回調, 把阮老師的demo實現了一下編程
const EVENT = {}; Function.prototype.on = function (eventName, cb) { EVENT[eventName] = cb; }; Function.prototype.trigger = function (eventName) { EVENT[eventName](); }; function f1() { setTimeout(() => { console.log("f1 start"); // 觸發事件 f1.trigger('done') }, 1000); } function f2() { console.log("f2"); } f1.on("done", f2); // 添加監聽 f1()
實現了功能解耦,其實仍是依靠回調函數, 只不過觸發方式變化了, 不是直接嵌套在上一步任務裏執行了promise
發佈訂閱基於事件監聽,發佈者和訂閱者經過一個事件中心進行通訊, 而且實現了多個事件解耦瀏覽器
/** * 發佈訂閱方式 * 維護一個事件中心進行通訊 */ const event = { // 事件中心 eventList: [], // 訂閱事件, 添加一個回調邏輯 on(type, fn) { if (!this.eventList[type]) { this.eventList[type] = []; } this.eventList[type].push(fn); }, // 發佈事件, 遍歷事件列表,去執行全部事件 emit(type, ...args) { const cbList = this.eventList[type]; if (!cbList || cbList.length === 0) return; cbList.forEach((cb) => { cb.apply(event, args); }); }, }; let data = {}; // 咱們能夠訂閱多個事件, 而且相比回調, 訂閱結合發佈徹底解耦了, 二者並沒有關聯性 event.on("change", (data) => { // 訂閱者1的邏輯 console.log("訂閱者1: data obj change", data); }); event.on("change", (data) => { // 訂閱者2的邏輯 if (Object.keys(data).length === 2) { console.log('訂閱者2: data s數據有兩個了', data) } }); // 發佈事件: 咱們能夠等待數據狀態發生變化或者 異步執行完去發佈 setTimeout(() => { data.name = 'huhua' // 發佈者, 我想在哪發就在哪發 event.emit('change', data) }, 1000); setTimeout(() => { data.age = '26' event.emit('change', data) }, 2000);
vue源碼中不是用到了嗎...那咱們動手寫寫, 看看發佈訂閱和觀察者模式的區別
/** * 觀察者模式的簡易實現 * 觀察者對象: 須要在被觀察者狀態變化時觸發更新邏輯 * 被觀察者對象: 須要收集全部的對本身進行觀測的觀察者對象 */ // 被觀察者 // 對於一個被觀察的人來講: 我要知道是哪些人在觀察我, 個人狀態怎麼樣 class Sub { constructor(name) { this.name = name; this.state = "pending"; this.observer = []; // 存放全部觀察者的集合 } // 添加觀察者 add(ob) { this.observer.push(ob); } // 更改狀態 setState(newState) { this.state = newState; // 狀態改了不起告訴全部觀察者啊, 其實就是執行觀察者對象的更新函數 this.notify(); } // 通知 notify() { this.observer.forEach((ob) => ob && ob.update(this)); } } // 觀察者 class Observer { constructor(name) { this.name = name; } update(sub) { console.log( `觀察者${this.name} 已收到被觀察者${sub.name}狀態改變了: ${sub.state}` ); } } let sub = new Sub('學生小麥') let ob1 = new Observer('語文老師') let ob2 = new Observer('數學老師') let ob3 = new Observer('英語老師') // 與發佈訂閱不一樣的是, 這裏被觀察者須要添加全部的觀察者對象, 以便在本身狀態改變時去執行觀察者的更新邏輯 // 兩者有關聯關係, 我要知道我被誰觀察 // 發佈訂閱中, 發佈者和訂閱者之間沒有關聯關係, 經過事件中心來管理 // 訂閱不須要知道誰去發佈 sub.add(ob1) sub.add(ob2) sub.add(ob3) sub.setState('fulfilled') // 觀察者語文老師 已收到被觀察者學生小麥狀態改變了: fulfilled // 觀察者數學老師 已收到被觀察者學生小麥狀態改變了: fulfilled // 觀察者英語老師 已收到被觀察者學生小麥狀態改變了: fulfilled sub.setState('rejected') // 觀察者語文老師 已收到被觀察者學生小麥狀態改變了: rejected // 觀察者數學老師 已收到被觀察者學生小麥狀態改變了: rejected // 觀察者英語老師 已收到被觀察者學生小麥狀態改變了: rejected
promise爲咱們提供了一種新的異步編程方式, 寫這篇文章目的也是爲了手動實現一個知足A+規範的promise對象;
咱們先來看一看promise A+規範 https://www.ituring.com.cn/ar...
其實promise的核心就是then方法, 源碼中也用到發佈訂閱模式思想, 經過then鏈的 鏈式回調將上一步結果透傳給下一步使用(返回了一個新的promise),
解決了回調地獄的問題
手寫promise正在進行中ing...到時候附上連接
async + await是最新的異步編程方案...比較符合咱們的編碼習慣
其實搞懂了generator函數和promise以後, async和await就很好懂了, 我後面也實現了一遍
附上連接:
https://github.com/appleguard...
仍是說一下:
generator函數是一個狀態機,封裝了多個內部狀態
generator函數除了狀態機,仍是一個遍歷器對象生成函數
可暫停函數, yield可暫停(保存上下文),next方法可啓動,每次返回的是yield後的表達式結果
yield表達式自己沒有返回值,next方法能夠帶一個參數,該參數就會被看成上一個yield表達式的返回值
async+await 就是一個被包裝的generator自執行器函數,結合promise實現