javascript異步編程總結

前言

Javascript語言的執行環境是「單線程」。javascript

單線程: 一次只能完成一個任務。若是有多個任務,就必須排隊,前面一個任務完成,再執行後面一個任務。html

單線程的好處是執行環境簡單,壞處是在一些耗時的任務上會堵塞進程。好比讀取一個大文件,線程卡在這個任務上,形成頁面卡死。java

那如何在讀取文件的同時,又能查看圖片,作一些其餘的事呢?編程

這就到了「同步」和「異步」之爭:
同步:後一個任務等待前一個任務結束,而後再執行,程序的執行順序與任務的排列順序是一致的、同步的。
異步:每個任務有一個或多個回調函數(callback),前一個任務結束後,不是執行後一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行,因此程序的執行順序與任務的排列順序是不一致的、異步的。promise

舉個栗子:
去餐廳吃飯
同步:你們依次排隊,前一我的付完錢,等待領取食物。。。漫長等待。。。食物作好了,後一我的跟進。
異步:你們依次排隊,前一我的付完錢,服務員給他一個餐牌,後一我的跟進。餐牌對應的食物作好了,再去領取。異步

如下總結了「異步編程」的4種方式:async

1. 回調函數

回調函數:異步編程的最基本的方式。模塊化

下面經過樣例做爲演示:咱們定義三個方法「作飯」(cook)、「吃飯」(eat),「洗碗」(wash)三個方法,它們是層層依賴的關係,下一步的的操做須要使用上一部操做的結果(這裏使用 setTimeout 模擬異步操做)。異步編程

// 作飯
function cook() {
    console.log('開始作飯...');

    sleep(2000);   // 等待2秒

    console.log('作飯完畢');
}

// 吃飯
function eat() {
    console.log('開始吃飯...');

    sleep(2000);   // 等待2秒

    console.log('吃飯完畢');
}

// 洗碗
function wash() {
    console.log('開始洗碗...');

    sleep(2000);   // 等待2秒

    console.log('洗碗完畢');
}

// 阻塞等待(毫秒)
function sleep(delay) {
    let start = (new Date()).getTime();

    while((new Date()).getTime() - start < delay) {
        continue;
    }
}

cook();
eat();
wash();

// 開始作飯...
// 作飯完畢
// 開始吃飯...
// 吃飯完畢
// 開始洗碗...
// 洗碗完畢

上面是「同步」的寫法,下面咱們改寫成「異步」:回調函數函數

// 作飯
function cook(callback) {
    console.log('開始作飯...');

    setTimeout(function() {
        console.log('作飯完畢');

        // 這裏是回調,執行吃飯的方法
        callback();

    }, 2000);
}

// 吃飯
function eat(callback) {
    console.log('開始吃飯...');

    setTimeout(function() {
        console.log('吃飯完畢');

        // 這裏是回調,執行洗碗的方法
        callback();
        
    }, 2000);
}

// 洗碗
function wash() {
    console.log('開始洗碗...');

    setTimeout(function() {
        console.log('洗碗完畢');

        // 洗碗以後的其餘動做,這裏就不寫了

    }, 2000);
}

cook(function() {
    eat(function() {
        wash();
    })
});

// 開始作飯...
// 作飯完畢
// 開始吃飯...
// 吃飯完畢
// 開始洗碗...
// 洗碗完畢

回調函數的優勢是簡單、容易理解和部署,缺點是不利於代碼的閱讀和維護,各個部分之間高度耦合(Coupling),流程會很混亂,並且每一個任務只能指定一個回調函數。

2. 事件監聽

事件監聽:採用事件驅動模式,任務的執行不取決於代碼的順序,而取決於某個事件是否發生。

let events = require('events');
let eventEmitter = new events.EventEmitter();

// 作飯
var cook = function() {
    console.log('開始作飯...');

    setTimeout(function() {
        console.log('作飯完畢');

        // 執行eat事件
        eventEmitter.emit('eatEvent');

    }, 2000);
}

// 吃飯
var eat = function() {
    console.log('開始吃飯...');

    setTimeout(function() {
        console.log('吃飯完畢');

        // 執行wash事件
        eventEmitter.emit('washEvent');

    }, 2000);
}

// 洗碗
var wash = function() {
    console.log('開始洗碗...');

    setTimeout(function() {
        console.log('洗碗完畢');

        // 洗碗以後的其餘動做,這裏就不寫了

    }, 2000);
}

// 綁定cook事件
eventEmitter.on('cookEvent', cook);
// 綁定eat事件
eventEmitter.on('eatEvent', eat);
// 綁定wash事件
eventEmitter.on('washEvent', wash);

// 執行cook事件
eventEmitter.emit('cookEvent');

// 開始作飯...
// 作飯完畢
// 開始吃飯...
// 吃飯完畢
// 開始洗碗...
// 洗碗完畢

這種方法的優勢是比較容易理解,能夠綁定多個事件,每一個事件能夠指定多個回調函數,並且能夠"去耦合"(Decoupling),有利於實現模塊化。缺點是整個程序都要變成事件驅動型,運行流程會變得很不清晰

3. 發佈/訂閱

發佈/訂閱:又稱「觀察者模式」。咱們假定,存在一個"信號中心",某個任務執行完成,就向信號中心"發佈"(publish)一個信號,其餘任務能夠向信號中心"訂閱"(subscribe)這個信號,從而知道何時本身能夠開始執行。

和上一個「事件監聽」很相似,本人對這種模式理解有限,這裏就不列代碼誤導你們了。

4. Promise

Promise: 由 CommonJS 小組的成員在 Promise/A 規範中提出,目的是爲異步編程提供統一接口。

根據 Promise/A 規範,promise 是一個對象,只須要 then 這一個方法。

// 作飯
function cook() {
    console.log('開始作飯...');

    let promise = new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('作飯完畢');
    
            resolve();
    
        }, 2000);
    });

    return promise;
}

// 吃飯
function eat(callback) {
    console.log('開始吃飯...');

    let promise = new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('吃飯完畢');
    
            resolve();
            
        }, 2000);
    });

    return promise;
}

// 洗碗
function wash() {
    console.log('開始洗碗...');

    let promise = new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('洗碗完畢');
    
            // 洗碗以後的其餘動做,這裏就不寫了
            resolve();
    
        }, 2000);
    });

    return promise;
}

cook()
.then(function() {
    return eat();
})
.then(function() {
    return wash();
})
.then(function() {
    console.log('結束...');
})
.catch(function() {
    console.log('好像出什麼問題了'); 
});

優勢:

  1. 回調函數變成了鏈式寫法,程序的流程能夠看得很清楚,並且有一整套的配套方法,能夠實現許多強大的功能。解決回調地獄(Callback Hell)問題
  2. 更好地進行錯誤捕獲

Promise的功能不單單隻上面用到的,諸如其餘all(), race()之類,限於篇幅,你們能夠翻看其餘文章查看。

結尾

參考文章:Javascript異步編程
參考文章:Promise使用詳解

相關文章
相關標籤/搜索