初始Promise/A+規範

長孫雨聰七星上將發表於2019-01-25

view繼承關係

個人部落格: http://zhangsunyucong.top

前言

這篇文章主要講兩個內容,一是,初步認識Promise,二是,Async模組和Async/Await的使用

什麼是Promise

Promise表示一個非同步操作的最終結果。一個Promise物件有一個then方法,then方法中返回一個Promise。

相關的概念

  • promise是一個包含了相容promise規範then方法的物件或函式,
  • thenable 是一個包含了then方法的物件或函式。
  • value 是任何Javascript值。 (包括 undefined, thenable, promise等).
  • exception 是由throw表示式丟擲來的值。
  • reason 是一個用於描述Promise被拒絕原因的值。

Promise的狀態

Promise有三種狀態:pending, fulfilled 或 rejected。pending是等待執行狀態,fulfilled是成功執行狀態,rejected是失敗執行狀態。

Promise只能從pending到fulfilled或者從pending到rejected狀態,當狀態發生改變時,promise.then(onFulfilled, onRejected)方法將被呼叫。Promise可以使用resolve或者reject將value或者reason作為下一個Promise的第一個回撥引數。

來個簡單的Promise基本用法:

var promise = new Promise(function(resolve, reject){
    //do something
    if(success){
        resolve(value);
    } else {
        reject(value);
    }
});

promise.then(function(value){
    //成功時呼叫
}, function(value){
    //失敗時呼叫
});
複製程式碼

上面的程式碼只是表示Promise用法的流程.

使用Promise/A+規範實現以下幾個功能

  • 上一步的結果可以作為下一步的引數
  • 出現異常時,能夠捕獲到異常
  • 可以在每一步進行流程控制

Promise的具體知識,可以參考這裡

下面介紹Async模組和ES7的Async/Await的使用

Async模組

Async模組的github地址:github.com/caolan/asyn…

配置好node的環境後(具體過程,自己百度),安裝Async模組

npm install --save async

Async模組提供了很多關於集合,流程控制,工具方法,這裡只體驗幾個常見的流程控制方法:series,parallel,waterfall,auto。其他方法的用法,可以檢視官方文件:文件地址

series的使用

series(tasks, callback)

tasks可以是陣列或者物件

series是序列執行tasks中的任務,如果有一個任務執行返回了錯誤資訊,則不再繼續執行後面未執行的任務,並將結果以陣列或者物件的形式傳給callback。具體結果的格式由你定義tasks時使用的是陣列還是物件。

    async.series([
        function (callback) {
            setTimeout(function () {
                console.log("one");
                callback(null, 'one');
            }, 300);
        },
        function (callback) {
            setTimeout(function () {
                console.log("two");
                callback(null, 'two');
            }, 200);
        },
        function (callback) {
            setTimeout(function () {
                console.log("three");
                callback(null, 'three');
            }, 100);
        }
    ], function (err, results) {
        console.log(err);
        console.log(results);
    });
複製程式碼

執行結果:

one two three null [ 'one', 'two', 'three' ]

當tasks是物件:

async.series({
        one: function (callback) {
            setTimeout(function () {
                console.log("one");
                callback(null, 'one');
            }, 300);
        },
        two: function (callback) {
            setTimeout(function () {
                console.log("two");
                callback(null, 'two');
            }, 200);
        },
        three: function (callback) {
            setTimeout(function () {
                console.log("three");
                callback(null, 'three');
            }, 100);
        }
    }, function (err, results) {
        if(err) {
            console.log("異常結束" + '結果為:' + results);
            return;
        }
        console.log(results);
    });
複製程式碼

執行結果:

one two three { one: 'one', two: 'two', three: 'three' }

上面程式碼中,從上到下的函式開始執行時間是逐漸減小的,而執行結果的輸出順序是one,two,three,說明series是序列執行任務的。

將第二個任務的程式碼改為以下的樣子:

function (callback) {
    console.log("two");
    setTimeout(function () {
        callback("errMsg", 'two');
    }, 200);
}
複製程式碼

執行的結果為:

one two errMsg [ 'one', 'two' ]

可以看到,當第二個任務返回了錯誤資訊,則不會再繼續執行後面未執行的任務

parallel的使用

parallel(tasks, callback)

tasks可以是一個陣列或者物件

parallel是並行執行多個任務,如果有一個任務執行返回了一個錯誤資訊,則不再繼續執行後面未執行的任務,並將結果以陣列或者物件的形式傳給callback。

async.parallel([
        function (callback) {
            setTimeout(function() {
                console.log('one');
                callback(null, 'one');
            }, 500);
        },
        function (callback) {
            setTimeout(function() {
                console.log('two');
                callback(null, 'two');
            }, 200);
        },
        function (callback) {
            setTimeout(function() {
                console.log('three');
                callback(null, 'three');
            }, 100);
        }
    ], function (err, results) {
        console.log(err);
        console.log(results);
    });
複製程式碼

執行結果為:

three two one null [ 'one', 'two', 'three' ]

結果中的輸出順序是three,two,one,說明parallel是並行執行任務的。

同樣,將第二個任務的程式碼改為:(陣列定義tasks)

function (callback) {
    setTimeout(function() {
        console.log('two');
        callback("errMsg", 'two');
    }, 200);
},
複製程式碼

執行的結果為:

three two errMsg [ <1 empty item>, 'two', 'three' ] one


將第二個任務程式碼改為:(陣列定義tasks)

function (callback) {
    setTimeout(function() {
        console.log('two');
        callback("errMsg", 'two');
    }, 200);
},
複製程式碼

將第三個任務程式碼改為:(陣列定義tasks)

function (callback) {
    setTimeout(function() {
        console.log('three');
        callback(null, 'three');
    }, 200);
}
複製程式碼

也就是,第三個的開始執行時間改成和出現錯誤資訊的第二個任務的時間一樣。

執行的結果為:

two errMsg [ <1 empty item>, 'two' ] three one

從結果中可以看出,當前面執行的未完成的任務會佔一個位置,而後面未完成的任務不會佔陣列的位置。

parallelLimit(tasks, limit, callback)

parallelLimit和parallel差不多,區別是它可以指定同時並行執行任務的最大數量。

    async.parallelLimit({
        one: function (callback) {
            setTimeout(function() {
                console.log('one');
                callback(null, 'one');
            }, 200);
        },
        two: function (callback) {
            setTimeout(function() {
                console.log('two');
                callback(error, 'two');
            }, 200);
        },
        three: function (callback) {
            setTimeout(function() {
                console.log('three');
                callback(null, 'three');
            }, 100);
        }
    }, 2, function (err, results) {
        console.log(err);
        console.log(results);
    });
複製程式碼

執行的結果為:

one two errMsg { one: 'one', two: 'two' } three

如果是tasks是陣列時,執行的結果是:

two errMsg [ <1 empty item>, 'two' ] one

由於同時並行執行任務的最大數量是2,由於第二個任務產生錯誤資訊,第三個任務還沒開始執行。另外如果要取最後回撥結果中的值,物件定義tasks可能會更好。

waterfall的使用

waterfall(tasks, callback)

tasks只能是陣列型別

waterfall會序列執行tasks中的任務,前一個任務的結果可以作為下一個任務的引數。

    async.waterfall([
        function (callback) {
            console.log("one");
            setTimeout(function() {
                callback(null, 'one', 'two');
            }, 200);
        },
        function (arg1, arg2, callback) {
            console.log("two" + '引數:' + "arg1是" + arg1 + "arg2是" + arg2);
            setTimeout(function() {
                callback(null, 'three');
            }, 200);
        },
        function (arg3, callback) {
            console.log("three" + '引數:' + "arg3是" + arg3);
            setTimeout(function() {
                callback(null, 'done', 'done1');
            }, 200);
        }
    ], function (err, results) {
        if(err) {
            console.log("異常結束" + '結果為:' + results);
            return;
        }
        console.log(results);
    });
複製程式碼

執行的結果是:

one two引數:arg1是onearg2是two three引數:arg3是three done

輸出的結果的順序是one,two, three,是序列執行的。前一個任務的結果可以作為下一個任務的引數。

注意一下,程式碼中控制檯輸出的one,two,three程式碼是移到了定時器的外面。

auto的使用

auto(tasks, concurrencyopt, callback)

auto可以序列和並行執行任務,可以定義任務之間的依賴關係。沒有依賴關係的任務會盡可能快的開始並行執行,序列是由於任務的依賴關係而實現的。concurrencyopt指定的是並行執行任務的最大數量。tasks只能是物件型別。

    async.auto({
        task1: function(callback) {
            setTimeout(function() {
                console.log('task1');
                callback(null, 'data', 'data1');
            }, 200);
        },
        task2: function(callback) {
            setTimeout(function () {
                console.log('task2');
                callback(null, 'data2');
            }, 100)
        },
        task3: ['task1', 'task2', function(results, callback) {
            console.log('task3', JSON.stringify(results));
            setTimeout(function () {
                console.log('task3');
                callback(null, 'data3');
            }, 200);

        }],
        task4: ['task3', function(results, callback) {
            console.log('task4', JSON.stringify(results));
            setTimeout(function () {
                console.log('task4');
                callback(null, {'task2':results.task2, 'task4':'data4'});
            }, 100);

        }]
    }, function(err, results) {
        console.log('err = ', err);
        console.log('results = ', results);
    });
複製程式碼

執行的結果是:

task2 task1 task3 {"task2":"data2","task1":["data","data1"]} task3 task4 {"task2":"data2","task1":["data","data1"],"task3":"data3"} task4 err = null results = { task2: 'data2', task1: [ 'data', 'data1' ], task3: 'data3', task4: { task2: 'data2', task4: 'data4' } }

task1和task2是不依賴於任何其他任務的,它們會盡可能的開始,而且由於它們是並行執行的,task2的開始時間較短,所以task2比task1先開始。task3依賴於task1和task2,所以task3等到task1和task2執行完畢後再執行。task4依賴task3,所以task4要等到task3執行完畢後再執行。

ES7的Async/Await

主要看它們的用法

var task1 = function () {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve("result");
        }, 1000);
    });
};

var excTask1 = async function () {
    console.log("start");
    console.log(await task1());
    console.log("end");
};

function awaitDemo() {
    excTask1();
}
複製程式碼

連續點選執行四次,執行的結果為:

start start start start result end result end result end result end

async代表是一個async函式,await只能用在async函式中,await等待一個Promise的返回,直到Promise返回了才會繼續執行await後面的程式碼。這裡的Promise利用setTimeout模擬非同步任務。

從輸出結果中可以看出,每次執行都是到await時,就停止等待Promise的返回,後再繼續執行await後面的程式碼。

相關文章