做爲一個新人小白來講,JS的異步處理讓我很頭疼。。promise
那麼什麼是異步,爲何產生,又怎麼處理呢?且看下文瀏覽器
單線程
做爲瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操做DOM。這決定了它只能是單線程,不然會帶來很複雜的同步問題。好比,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另外一個線程刪除了這個節點,這時瀏覽器應該以哪一個線程爲準?
bash
單線程最大的好處就是實現簡單,執行環境單純dom
可是也有一個很致命的缺點:當一個線程須要執行很長時間,那麼後續線程就須要一直等着,若是這個線程陷入了死循環,那麼後續線程都將得不到執行的機會,頁面卡死。異步
因此爲了不這個問題,JS制定了兩種執行模式,同步模式和異步模式async
異步與同步
同步模式 就是上一段的模式,後一個任務等待前一個任務結束,而後再執行,程序的執行的調入和完成順序與任務的排列順序是一致的、同步的;函數
異步模式 則徹底不一樣,當一個任務執行時,若它執行時間很短,則它執行完後,後面任務緊跟着它執行,若它涉及異步操做,則將此任務調入主線程隊列,異步操做給相應的異步操做理模塊執行,與此同時調取下一個任務執行,等到異步模塊處理完異步操做後,給主線程一個成功或失敗信號,再將異步操做結果調入主線程隊列排隊,直到執行完。(具體流程參見js事件輪詢機制 -_-||)
測試
因此在我理解,異步的關鍵就是如何實如今異步完成後返回這個信號,來告訴主線程能夠進行異步操做結果的處理。ui
1. 回調函數this
回調函數 就是將A函數做爲B函數的參數傳遞給B,等B函數執行完成後再執行
好比我想實現B函數實現一系列複雜運算(或異步操做),實現完成後改變dom,而A函數須要在B函數更新完dom後,再對dom執行一些操做
用回調實現:
var domList = [];
function B(dom,callback){
setTimeout(()=>{
dom.push(1);
callback();
},2000);
}
function A(){
console.log(domList);
}
B(domList,A)複製代碼
這裏用延遲執行來代替異步操做,上面結果在兩秒後打印 [1],在B中的callback調用這裏,就是手動模擬了異步操做完成的信號,告訴A能夠進行後續操做了。
注意:處理異步能夠用回調,可是回調不必定就是異步
2. 事件監聽
採起事件驅動模式,即定義一個事件,當這個事件發生時,執行一些處理。
在異步中的實現即 先定義事件發生時的處理函數而且監聽這個事件,當進入異步且執行完後觸發事件,進而觸發監聽這個事件相關聯的處理函數。
監聽的函數有:on、bind、listen、addEventaddEventListener、observe
仍是上述功能,用事件監聽實現:
var domList = [];
function A(){
console.log(domList);
}
// 綁定監聽 當B發生updateComplate事件時,執行A函數
B.on('updateComplate',A);
function B(){
setTimeout(()=>{
domList.push(1);
B.trigger('updateComplate');
},2000);
}
A();複製代碼
這種方式容易理解,也能夠爲一個事件監聽綁定多個事件處理函數
可是程序會變成事件驅動型的,運行流程不清晰
3. 發佈/訂閱
發佈者發出通知 =>主題對象收到通知並推送給訂閱者 => 訂閱者執行相應的操做。
// 一個發佈者 publisher,功能就是負責發佈消息 - publish
var pub = {
publish: function () {
dep.notify();
}
}
// 多個訂閱者 subscribers, 在發佈者發佈消息以後執行函數
var sub1 = {
update: function () {
console.log(1);
}
}
var sub2 = {
update: function () {
console.log(2);
}
}
var sub3 = {
update: function () {
console.log(3);
}
}
// 一個主題對象
function Dep() {
this.subs = [sub1, sub2, sub3];
}
Dep.prototype.notify = function () {
this.subs.forEach(function (sub) {
sub.update();
});
}
// 發佈者發佈消息, 主題對象執行notify方法,進而觸發訂閱者執行Update方法
var dep = new Dep();
pub.publish();複製代碼
4. promise對象
promise對象用於處理異步操做
它表明一個未完成、可是預計未來會完成的操做
三種狀態:
對上述功能的實現:
var domList = [];
function B(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
domList.push(1);
resolve(domList);
},2000);
});
}
function A(){
console.log(domList);
}
B().then((result)=>{
A();
}).catch(err=>{
console.log(err)
});複製代碼
promise中,resolve和reject至關於異步操做完成的觸發信號,then至關於resolve後的成功處理函數,catch至關於reject後的失敗處理函數
promise的then能夠鏈式調用
var domList = [];
function B(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
domList.push(1);
resolve(domList);
},2000);
});
}
function A(){
console.log(domList);
domList.push(2);
}
B().then((result)=>{
A();
}).then((result)=>{
A();
}).catch(err=>{
console.log(err)
});複製代碼
5. async/await
初識async/await是在實際代碼中,是怎麼寫的呢,async定義一個函數,在裏面 使用await 可讓後面的代碼等待此行執行完後再執行,我去,這我一理解,那我須要耗時的函數定義爲async ,實際耗時操做加一個await,那不是分分鐘變異步爲同步了嗎(-_-'')
因而年輕的我開始寫測試了
async function test(){
console.log(1);
await setTimeout(()=>{
console.log(2);
},2000);
console.log(3);
}
test();複製代碼
test執行應該是 1 ,兩秒後 2,3 了唄,結果呢。。。。 1,3,兩秒後 2,控制檯還多了個什麼Promise
好吧,仍是安安靜靜回去看教程吧!!
使用方法:
function asyncF(){
return new Promise((resove,reject)=>{
setTimeout(()=>{
resove('2');
},2000);
})
}
async function callAsync(){
console.log('1')
await asyncF().then(result=>{
console.log(result);
});
console.log('3');
}
callAsync();複製代碼
這樣就能夠執行了
async/await基於promise實現的,那他比promise有哪些優點呢?(畢竟新整的東西還不如之前的還不如不整)
function asyncF(x) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2*x);
}, 2000);
})
}
async function callAsync() {
let r1 = await asyncF(1);
let r2 = await asyncF(r1);
let r3 = await asyncF(r2);
console.log(r3)
}
callAsync();複製代碼
相似同步的異步使用,以及async函數對promise對象的自動轉換操做簡便