promise異步編程介紹

promise異步編程介紹

Promise 是什麼?

Promise 對象用來進行延遲(deferred) 和 異步(asynchronous) 計算。

解決的問題

回調函數是JavaScript的一大特色! node.js官方的api基本都是以會回調方式傳遞函數返回值。習慣同步編程的對這種異步方式多少會產生水土不服,而且層層嵌套,不知不覺就建造起了一座高高的回調金字塔。針對這種普遍問題,Promise應勢而生!

我們舉個例子

有以下的業務場景,從服務器中,依次請求三張圖片,每次有一張圖片加載完成,就打印結果。

一般正常的寫法

    /**
     * [loadimg 加載一張圖片]
     * @param  {[type]}   url      [description]
     * @param  {Function} callback [description]
     * @return {[type]}            [description]
     */
    function loadimg(url, callback) {
        var img = new Image();
        img.src = url;
        img.onload = callback;
    }

    //開始加載圖片
    loadimg("img/a.png", function() {
        console.log("圖片a已經加載完畢");
        loadimg("img/b.png", function() {
            console.log("圖片b已經加載完畢");
            loadimg("img/c.png", function() {
                console.log("圖片c已經加載完畢");
                console.log("a,b,c三張圖片已經完全加載完畢");
            });
        });
    });

ps:是不是很恐怖

採用promise的寫法

    /**
     * [loadimg 加載一張圖片]
     * @param  {[type]}   url      [description]
     * @param  {Function} callback [description]
     * @return {[type]}            [description]
     */
    function loadimg(url) {
        return new Promise(function(resolve, reject) {
            var img = new Image();
            img.src = url;
            img.onload = function() {
                resolve();
            };
        });
    }
    //開始加載圖片
    loadimg("img/a.png").then(function() {
        console.log("圖片a已經加載完畢");
        return loadimg("img/b.png");
    }).then(function() {
        console.log("圖片b已經加載完畢");
        return loadimg("img/c.png");
    }).then(function() {
        console.log("圖片c已經加載完畢");
        console.log("a,b,c三張圖片已經完全加載完畢");
    });

基本用法(then方法)

Promise/A+規範
  • 一個promise可能有三種狀態:等待(pending)、已完成(fulfilled)、已拒絕(rejected)
  • 一個promise的狀態只可能從“等待”轉到“完成”態或者“拒絕”態,不能逆向轉換,同時“完成”態和“拒絕”態不能相互轉換
  • Promise構造函數接受一個函數作爲參數,該函數的兩個參數分別是resolve方法和reject方法。如果異步操作成功,則用resolve方法將Promise對象的狀態變爲“成功”(即從pending變爲resolved);如果異步操作失敗,則用reject方法將狀態變爲“失敗”(即從pending變爲rejected)。
  • promise必須實現then方法(可以說,then就是promise的核心),而且then必須返回一個promise,同一個promise的then可以調用多次,並且回調的執行順序跟它們被定義時的順序一致
  • then方法接受兩個參數,第一個參數是成功時的回調,在promise由“等待”態轉換到“完成”態時調用,另一個是失敗時的回調,在promise由“等待”態轉換到“拒絕”態時調用。同時,then可以接受另一個promise傳入,也接受一個“類then”的對象或方法,即thenable對象。

創建Promise

    var promise = new Promise(function(resolve, reject) {
        // 做一些異步操作的事情,在這裏做了一個定時時間爲1000ms的定時器任務
        setTimeout(function() {
            if ( /* 一切正常 */ false) {
                resolve("promise worked!");
            } else {
                reject("promise failed");
            }
        }, 1000);
    });

Promise 的構造器接受一個函數作爲參數,它會傳遞給這個回調函數兩個變量 resolve 和 reject。在回調函數中做一些異步操作

成功之後調用對象的then方法,該方法中有兩個參數 1. resolve(val), val的返回成功的參數

否則調用 2.reject() //處理業務錯誤時的函數

獲取異步完成後的參數

  promise.then(function(val) {
        // success
        console.log(val);
    }, function(val) {
        // failure
        console.log(val);
    });

promise catch 方法

catch 方法是 then(null,reject)中的reject處理的替代方法,用於指定發生錯誤時的回調函數

ps: promise對象的錯誤具有冒泡性質,會一直向後傳遞,直到被捕獲爲止

在這個例子中,創建一個Promise 發出一個異常消息

    var promise = new Promise(function(resolve, reject) {
        // resolve("ok");
        reject("false");  //發出一個錯誤的異常消息
    });

在這裏我們使用catch來捕獲錯誤

    promise.then(function(str){
       console.log("正確結果"+str);
    }).catch(function(str){
      console.log("異常結果"+str);
    });

在這裏我們使用then(null,reject) 中的reject來捕獲錯誤

    promise.then(function(str) {
        console.log("正確結果" + str);
    }, function(str) {
        console.log("異常結果" + str);
    });

all方法

Promise.all方法接受一個數組作爲參數,p1、p2、p3都是Promise對象的實例。(Promise.all方法的參數不一定是數組,但是必須具有iterator接口(可遍歷對象接口),且返回的每個成員都是Promise實例。)

p的狀態由p1、p2、p3決定,分成兩種情況。

(1)只有p1、p2、p3的狀態都變成fulfilled,p的狀態纔會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。

(2)只要p1、p2、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。

    /**
     * [createPromise 創建一個promise對象]
     * @param  {[type]} str [description]
     * @return {[type]}     [description]
     */
    function createPromise(str) {
        return new Promise(function(resolve, reject) {
            setTimeout(function() {
                if (true) {
                    resolve(str); //返回標誌位
                } else {
                    reject();
                }
            }, 1000);
        });
    }

    var promise1 = createPromise("p1"),
        promise2 = createPromise("p2"),
        promise3 = createPromise("p3");
    //通過all方法將三個promise實例包裝成新的promise實例
    var promise = Promise.all([promise1, promise2, promise3]);
    promise.then(function(results) {
        console.log(results); // ["p1", "p2", "p3"]
    });

race方法

只要p1、p2、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的Promise實例的返回值,就傳遞給p的返回值。

    /**
     * [createPromise 創建一個promise對象,通過傳入不同的時間,來區別回調的時間]
     * @param  {[type]} str    [description]
     * @param  {[type]} second [description]
     * @return {[type]}        [description]
     */
    function createPromise(str, second) {
        return new Promise(function(resolve, reject) {
            setTimeout(function() {
                if (true) {
                    resolve(str); //返回標誌位
                } else {
                    reject();
                }
            }, second);
        });
    }

    var promise1 = createPromise("p1", 5000),
        promise2 = createPromise("p2", 1000),
        promise3 = createPromise("p3", 2000);
    //通過race方法將三個promise實例包裝成新的promise實例
    var promise = Promise.race([promise1, promise2, promise3]);
    promise.then(function(results) {
        console.log(results); // "p2"
    });

Promise.resolve方法 Promise.reject方法

有時需要將現有對象轉爲Promise對象,我們可以這樣做

var p = Promise.resolve('this is promise');

下面的代碼生成一個新的Promise對象的實例p,它的狀態爲fulfilled/rejected,所以回調函數會立即執行,Promise.resolve方法的參數就是回調函數的參數。

如果Promise.resolve方法的參數是一個Promise對象的實例,則會被原封不動地返回。

Promise.reject(reason)方法也會返回一個新的Promise實例,該實例的狀態爲rejected。Promise.reject方法的參數reason,會被傳遞給實例的回調函數。

    var p = Promise.resolve('this is promise');
    var p = Promise.reject('the promise is error');

    p.then(function(result) {
        console.log(result); //捕獲正常結果
    }).catch(function(result) {
        console.log(result); //捕獲錯誤結果
    });

兼容不支持promise的環境

在html中引入

<script src="https://www.promisejs.org/polyfills/promise-7.0.4.min.js"></script>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章