【原】談談promise

    最近在看《你不知道的javascript中卷》,發覺做者花了基本一半的篇幅去講異步和promise,以爲有必要總結一下。javascript

 其實本文的目的是想手寫一個Promise的,無奈總結着總結着發覺篇幅有點長,所以只好一分爲二,先介紹promise的用法,知道怎麼用,咱們才知道怎麼寫,因此把手寫一個promise的任務放到了下一篇文章當中。java

  固然,網上有不少關於promise的文章,均可以參考參考,有誤之處,歡迎之處。ajax

什麼是Prmoise

  promise是ES6新增的一個特徵,它已被列入ES6的正式規範中數組

  Promise對象能夠理解爲一次執行的異步操做,使用promise對象以後可使用一種鏈式調用的方式來組織代碼;讓代碼更加的直觀。也就是說,有了Promise對象,就能夠將異步操做以同步的操做的流程表達出來,避免了層層嵌套的回調函數。promise

示例:未使用promise,回調必須層層嵌套瀏覽器

$.ajax(url1, function(data1){
    // do something...
    $.ajax(url2, function(data2){
        // do something...
        $.ajax(url3, function(data3){
            // do something...
            done(data3); // 返回數據
        })
    });
});

 若是有多個嵌套,致使代碼不夠直觀,並且若是幾個操做以前沒有先後順序之分,須要等待上一個操做完成才能夠進行下一個操做,形成沒必要要的等待異步

promise就是爲了解決這些問題而產生的。async

 

Promise對象的特色:

    一、對象的狀態不受外界影響。函數

  Promise對象表明一個異步操做,有三種狀態測試

  • pending(執行中)
  • Resolved(成功,又稱Fulfilled)
  • rejected(拒絕)

其中pending爲初始狀態,fulfilled和rejected爲結束狀態(結束狀態表示promise的生命週期已結束)。

promise只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態.。

   二、一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。

 Promise對象的狀態改變,只有兩種可能:從Pending變爲Resolved和從Pending變爲Rejected

pending->fulfilled,pending->rejected。

只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果

 

Promise對象的缺點:

一、沒法取消Promise,一旦新建它就會當即執行,沒法中途取消。

二、若是不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。

三、當處於Pending狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)。

 

promise兼容性

除了IE這種古老的瀏覽器和一些低版本的安卓外,現代瀏覽器支持仍是挺好的,因此咱們能夠在谷歌的控制檯直接測試咱們的代碼

Promise的使用

先提早說明一下,下面的代碼示例,均可以複製到谷歌的控制檯就行測試!!

 

一、基本用法:

(1)、首先咱們new一個Promise,將Promise實例化

(2)、而後在實例化的promise能夠傳兩個參數,一個是成功以後的resolve,一個是失敗以後的reject

(3)、Promise實例生成之後,能夠用then方法分別指定Resolved狀態和Reject狀態的回調函數

代碼以下:

var promise = function(isReady){
    return new Promise(function(resolve, reject){
        // do somthing, maybe async
        if (isReady){
          return resolve('hello world');
        } else {
          return reject('failure');
        }
    });
}
 
//Promise實例生成之後,能夠用then方法分別指定Resolved狀態和Reject狀態的回調函數。
promise(true).then(function(value){
    // success,這裏是resolve的回調函數
    console.log(value);  //hello world
}, function(err){
    // failure,這裏是reject的回調函數
    console.log(err)
})

上述代碼是執行成功,返回hello world,若是想測試一下失敗後的返回值,能夠在promise(true).then...這裏改成 promise(false).then...便可 

 

二、鏈式操做

也許你會說,Promise只是簡化層層回調的寫法而已吧,其實否則,它的精髓是經過維護狀態、傳遞狀態的方式來使回調方式可以及時的調用,所以,相比於callback,它更靈活,更簡單。下面咱們來看看Promise的鏈式操做:

makePromise1()
.then(function(value){
    console.log(value);
    return makePromise2();
})
.then(function(value){
    console.log(value);
    return makePromise3();
})
.then(function(value){
    console.log(value);
});

function makePromise1(){
    var p = new Promise(function(resolve, reject){
        //異步操做
        setTimeout(function(){
            console.log('異步任務1');
            resolve('異步任務1傳過來的值');
        }, 2000);
    });
    return p;            
}
function makePromise2(){
    var p = new Promise(function(resolve, reject){
        //異步操做
        setTimeout(function(){
            console.log('異步任務2');
            resolve('異步任務2傳過來的值');
        }, 2000);
    });
    return p;            
}
function makePromise3(){
    var p = new Promise(function(resolve, reject){
        //異步操做
        setTimeout(function(){
            console.log('異步任務3');
            resolve('異步任務3傳過來的值');
        }, 2000);
    });
    return p;            
}

上面的代碼中,咱們有三個異步操做,makePromise1,makePromise2,makePromise3。其中第二個和第三個依次執行,也就是上一個操做完成以後才能夠進行。

輸出的值爲:

異步任務1
異步任務1傳過來的值
異步任務2
異步任務2傳過來的值
異步任務3
異步任務3傳過來的值

 

三、Promise的catch方法

var promise = function(isReady){
    return new Promise(function(resolve, reject){
        // do somthing, maybe async
        if (isReady){
          return resolve('hello world');
        } else {
          return reject('failure');
        }
    });
}
promise(true)
.then(function(value){
    console.log('resolved');
    console.log(value);
    console.log(haha); //此處的haha未定義
})
.catch(function(error){
    console.log('rejected');
    console.log(error);
});

     catch 方法是 then(onFulfilled, onRejected) 方法當中 onRejected 函數的一個簡單的寫法,也就是說能夠寫成 then(fn).catch(fn),至關於 then(fn).then(null, fn)

     使用 catch 的寫法比通常的寫法更加清晰明確,其實能夠類比成try/catch,這樣,其中有報錯的地方不會阻塞運行。好比定義了一個未定義haha,正常來講它上面的代碼也不會運行,由於被這個報錯阻塞了,有了catch,它上面的代碼能夠正常運行下去:

控制檯打印出來的東西:

resolved
hello world
rejected
ReferenceError: haha is not defined(…)

 

四、promise.all方法

  Promise.all 能夠接收一個元素爲 Promise 對象的數組做爲參數,當這個數組裏面全部的 Promise 對象都變爲 resolve 時,該方法纔會返回。

代碼示例:

var p1 = new Promise(function (resolve) {
    setTimeout(function () {
        resolve("第一個promise");
    }, 3000);
});

var p2 = new Promise(function (resolve) {
    setTimeout(function () {
        resolve("第二個promise");
    }, 1000);
});

Promise.all([p1, p2]).then(function (result) {
    console.log(result); // ["第一個promise", "第二個promise"]
});

     上面的代碼中,all接收一個數組做爲參數,p1,p2是並行執行的,等兩個都執行完了,纔會進入到then,all會把全部的結果放到一個數組中返回,因此咱們打印出咱們的結果爲一個數組。

  值得注意的是,雖然p2的執行順序比p1快,可是all會按照參數裏面的數組順序來返回結果。all的使用場景相似於,玩遊戲的時候,須要提早將遊戲須要的資源提早準備好,才進行頁面的初始化。

 

五、promise.race方法

      race的中文意思爲賽跑,也就是說,看誰跑的快,跑的快的就贏了。所以,promise.race也是傳入一個數組,可是與promise.all不一樣的是,race只返回跑的快的值,也就是說result返回比較快執行的那個。

var p1 = new Promise(function (resolve) {
    setTimeout(function () {
        console.log(1);
        resolve("第一個promise");
    }, 3000);
});

var p2 = new Promise(function (resolve) {
    setTimeout(function () {
        console.log(2);
        resolve("第二個promise");
    }, 1000);
});

Promise.race([p1, p2]).then(function (result) {
    console.log(result); 
});

// 結果:
// 2
// 第二個promise
// 1

能夠看到,傳的值中,只有p2的返回了,可是p1沒有中止,依然有執行。

race的應用場景爲,好比咱們能夠設置爲網路請求超時。寫兩個promise,若是在必定的時間內若是成功的那個咱們沒有執行到,咱們就執行失敗的那個,這裏再也不舉例子,能夠看看阮一峯的ES入門。

 

ES6的介紹就到這裏了,下一篇文章咱們來手寫一個promise

相關文章
相關標籤/搜索