JS異步處理

1、前言

做爲一個新人小白來講,JS的異步處理讓我很頭疼。。promise

那麼什麼是異步,爲何產生,又怎麼處理呢?且看下文瀏覽器

單線程

做爲瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操做DOM。這決定了它只能是單線程,不然會帶來很複雜的同步問題。好比,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另外一個線程刪除了這個節點,這時瀏覽器應該以哪一個線程爲準?
bash

單線程最大的好處就是實現簡單,執行環境單純dom

可是也有一個很致命的缺點:當一個線程須要執行很長時間,那麼後續線程就須要一直等着,若是這個線程陷入了死循環,那麼後續線程都將得不到執行的機會,頁面卡死。異步

因此爲了不這個問題,JS制定了兩種執行模式,同步模式和異步模式async

異步與同步

同步模式 就是上一段的模式,後一個任務等待前一個任務結束,而後再執行,程序的執行的調入和完成順序與任務的排列順序是一致的、同步的;函數

異步模式 則徹底不一樣,當一個任務執行時,若它執行時間很短,則它執行完後,後面任務緊跟着它執行,若它涉及異步操做,則將此任務調入主線程隊列,異步操做給相應的異步操做理模塊執行,與此同時調取下一個任務執行,等到異步模塊處理完異步操做後,給主線程一個成功或失敗信號,再將異步操做結果調入主線程隊列排隊,直到執行完。(具體流程參見js事件輪詢機制 -_-||)
測試

因此在我理解,異步的關鍵就是如何實如今異步完成後返回這個信號,來告訴主線程能夠進行異步操做結果的處理。ui

2、異步實現方式

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對象用於處理異步操做

它表明一個未完成、可是預計未來會完成的操做

三種狀態

  • pending:初始值,不是resolved,也不是rejected
  • resolved:操做成功
  • rejected:操做失敗
狀態轉換
  • 由pending轉換爲resolved
  • 由pending轉換爲rejected
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

好吧,仍是安安靜靜回去看教程吧!!

使用方法

  • async放於function定義以前,await寫在async函數中,單獨放在外層是不起做用的
  • async/await基於promise實現,被定義的異步函數執行後返回promise(這就能解釋爲啥多打印出來個promise對象了),若成功,使用.then()來獲取結果,不成功呢,用.catch()獲取失敗結果
  • await等待的後續結果必須返回一個promise,通常的函數調用無論用(。。。好吧,基於promise實現的,證實人家認識promise,願意等promise,不認識的還不樂意等了,這就是爲啥我等延時執行無論用)
因此要想實現上述功能,得讓await等的是個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有哪些優點呢?(畢竟新整的東西還不如之前的還不如不整)

  • async/await更加語義化,async 是「異步」的簡寫,async function 用於申明一個 function 是異步的; await,能夠認爲是async wait的簡寫, 用於等待一個異步方法執行完成;
  • async/await是一個用同步思惟解決異步問題的方案(等結果出來以後,代碼纔會繼續往下執行)
  • 能夠經過多層 async function 的同步寫法代替傳統的callback嵌套

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對象的自動轉換操做簡便

相關文章
相關標籤/搜索