【原】談談promise


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

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

  當然,網上有很多關於promise的文章,都可以參考參考,有誤之處,歡迎之處。

什么是Prmoise

  promise是ES6新增的一個特征,它已被列入ES6的正式規范中

  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就是為了解決這些問題而產生的。

 

Promise對象的特點:

    1、對象的狀態不受外界影響。

  Promise對象代表一個異步操作,有三種狀態

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

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

promise只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態.。

   2、一旦狀態改變,就不會再變,任何時候都可以得到這個結果。

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

pending->fulfilled,pending->rejected。

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

 

Promise對象的缺點:

1、無法取消Promise,一旦新建它就會立即執行,無法中途取消。

2、如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。

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

 

promise兼容性

除了IE這種古老的瀏覽器和一些低版本的安卓外,現代瀏覽器支持還是挺好的,所以我們可以在谷歌的控制台直接測試我們的代碼

Promise的使用

先提前說明一下,下面的代碼示例,都可以復制到谷歌的控制台就行測試!!

 

1、基本用法:

(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...即可 

 

2、鏈式操作

也許你會說,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傳過來的值

 

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(…)

 

4、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的使用場景類似於,玩游戲的時候,需要提前將游戲需要的資源提前准備好,才進行頁面的初始化。

 

5、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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM