寫在前面
JS因為是單線程的,所以在執行事務的時候,往往會因為某個事務的延遲,而導致服務器假死,這時候異步編程就顯的格外重要,但是異步編程一般理解為回調函數callback,典型的就是node,回調函數的層層嵌套又導致程序過於冗余,因為閉包的存在,導致了內存的泄露或者誤改上一層回調函數的參數,於是又有一個疑問,能不能用同步的方式去寫異步,ES6的promise就是以同步流程的方式寫出異步操做,但是piomise原生操作寫起來比較長,能不能簡介操作promise,ES7就又出現async/await的概念,與async/await同等語法糖作用的有依賴co庫的generator函數,generator函數也是用來解決異步操作,不過得依賴co庫,co 函數庫是著名程序員 TJ Holowaychuk 於2013年6月發布的一個小工具,用於generator函數的自動執行。
好了,那就羅列出js的異步操作
1、回調函數
ajax典型的異步操作,利用XMLHttpRequest,回調函數獲取服務器的數據傳給前台
之前也分析過ajax(飛機票)
回調函數的含義就是耗時任務f1中執行f2,f1不會堵塞住,而是先執行f2,再延遲執行f1
2、事件監聽
addEventListener
當監聽事件發生時,先執行回調函數,再對監聽事件進行改寫
3、觀察者模式,也叫訂閱發布模式
多個觀察者可以訂閱同一個主題,主題對象改變時,主題對象就會通知這個觀察者
其中步驟包括,訂閱、發布、退訂;先訂閱(subscribe)一個主題對象,根據主題對象發布(publish)內容,期間也退訂(unsubscribe)主題對象,一旦退訂就無法再次發布
可以把訂閱一個主題對象理解成監聽一個事件
觀察者模式的一個特點就是一旦主題事件一改變,就會通知整個觀察者;觀察者模式還可以計算出訂閱事件的個數
4、promise
上面也提到了,promise是異步編程的解決方案,是一種容器,保存着異步操作的結果,可以把異步函數以同步函數的形式寫出來
promise第一個特點:對象狀態不受外界影響,有三個狀態pending(),fulfilled(),rejected(),只有異步操作才會更改這個狀態,其他操作無法改變這個狀態
promise第二個特點:一旦狀態改變,pending->fulfilled或pending->rejected,狀態就會凝固住,稱為resolve,通過promise的回調函數可以立即得到這個結果,與事件監聽不同,一旦事件錯誤,就無法再次監聽
promise第三個特點:避免了回調函數的層層嵌套,實際上寫promise時,雖然沒有回到函數的層層嵌套,但是又有then的嵌套,這個又有新的解決方法
promise第一個缺點:一旦promise建立,就會立即執行,無法中途停止
舉個例子,假如去淘寶買東西,去看上一個東西,想要買,已經點擊確定購買了,但是你又cancle了,突然你發現工資發了,你又發送一個請求說又要買了,服務器接收到了你的第一個異步請求,你又有異步請求,異步請求又不能停止,求服務器陰影面積?
promise的第二個缺點:promise不設置回調函數,拋出的錯誤無法在外部捕獲
piomise的第三個缺點:處於pending狀態,無法知道進展到哪個狀態
下面一個經典的案例:用promise寫出ajax,就體現了promise的特點
function getJSON(url){ var promise = new Promise(function(resolve,reject){ var xhr = new XMLHttpRequest(); xhr.open("get",url); xhr.onreadystatechange=ajax; xhr.responseType = "json"; xhr.setRequestHeader("Accept", "application/json"); xhr.send(); function ajax(response){ if (this.readyState !=4) { return; } if (this.status ==200) { resolve(this.response) }else{ reject(new Error(this.status.responseText)) } } }) return promise; } getJSON("/new2").then(function(value){ console.log(value); },function(error){ console.log(error); })
5、es7語法糖async/await
async異步函數是promise的完成狀態,async函數直接then去獲取狀態改變值,catch來獲取錯誤
await只允許在async內部使用,就是async異步函數內部想要繼續then,就可以采用await異步函數,await異步函數是內部的async異步函數
async極大精簡了promise的操作
async function asyncFn(){ return 123; } asyncFn() .then(x => {console.log(x)}) .catch(err => console.log(error))
這是未精簡的promise操作,功能是把最終結果return最外層的promise,但是 用到了多層嵌套,比較復雜
const fn = () => { return getJSON() .then(data =>{ if(data.params){ return otherGetJSON(data).then(otherdata =>{ console.log(otherdata); return otherdata; }) }else{ console.log(data) } }) }
而async異步函數,直接去return出結果就行,這就是await是async內部async異步函數
const fn = async () => { const data = await getJSON(); if(data.params){ const otherdata = await otherGetJSON(data); console.log(otherdata); return otherdata; }else{ console.log(data) return data; } }
6、co庫的generator函數
generator函數是一個異步函數,只有異步操作有結果才會交還執行權
generator用到了ES6的遍歷Iterator的概念,創建一個指針對象,指向數據結構的起始位置,每次next指向下一個指針結構成員,直至指向的下一個結構成員為undefined
generator概念就是,每次遍歷讀用next方法,內部指針從結構頭部指向下一個結構成員,直至下一個結構成員為undefined,遇到yield或return時會返回value和done參數,value表示yield或return的值,done表示是否結束
function * gen(x){ var y = yield x +2; return y; } var g = gen(1); console.log(g.next()) // { value: 3, done: false } console.log(g.next()) // { value: undefined, done: true }
yield是遍歷的停止標志
而generator中的yield*表示 yield* 后面跟着一個可遍歷的結構
寫在后面
最近的review,讓我對js又有新的認識,js異步操作是js的核心所在,也是js迷人的地方,async/await和generator都是promise的語法糖,我覺得沒有優劣之分,可能道行太淺,還需刷道,希望業界前輩多多指責我,讓我進步更快。
好了,今天的交流就結束了,下次復習js定時器的內存分配等。