娓娓道來Promise

lixinfang101發表於2018-03-15

1.概述

      在Promise之前,我們js解決非同步的方式是採用回撥函式的方式,但是這種方式有個問題,就是當業務比較複雜時,很容易出現回撥地獄,使得程式碼很難理解和維護。所以,Promise就是解決回撥地獄的問題而出現的非同步程式設計的解決方案。

2.詳述Promise

我們先通過一個簡單的例子,來感受一下Promise。
let p = new Promise(function(resolve,reject){
    
    if(非同步操作成功){
        resolve();
    }else{
        reject();
    }
    
});
複製程式碼
通過上面的簡短的例子中,只需要注意三個地方:Promise(),resolve(),reject()

2.1 Promise()

Promise()建構函式在new的時候,會傳遞一個函式作為引數,那麼這個函式在新建一個Promise後,就會立即執行。
let p = new Promise(function(resolve,reject){
    console.log(1);
});
console.log(2);
複製程式碼
1
2

2.2 Promise的三種狀態

Promise物件有三種狀態:pending(進行中),resolved(已完成),rejected(已失敗)。
一旦狀態由pending變成了resolved了,狀態就凝固了,不會再改變了,會一直保持這個狀態。一旦狀態由pending變成了rejected了,狀態就凝固了,也不會再改變了。
在宣告一個Promise物件例項時,我們傳入的函式引數中,resolve對應著完成態之後的操作,reject對應著失敗態之後的操作。

2.3 then()方法

then()方法是Promise例項上的方法,then()方法是非同步的方法,需要自己呼叫,呼叫時傳遞兩個引數:第一個引數(函式)對應著完成態的操作,也就是resolve。第二個引數(函式)對應著失敗態的操作,也就是reject。
也就是說,在Promise中是通過then()方法來指定處理非同步操作結果的方法。
then()方法的返回值是什麼吶?then方法會返回一個新的Promise例項,這樣的設計好處是可以使用鏈式寫法,還有一點是鏈式寫法中的then方法(第二個開始),它們的resolve中的引數是什麼吶?就是前一個then()中的resolve的return返回值。
let p1 = new Promise(function(resolve,reject){
    setTimeout(()=>{
        resolve('p1');
    },10);
});
p1.then(data=>{
    console.log(data);  //p1
    return 'then1';
}).then(data=>{
    console.log(data);  //then1
    return 'then2';
}).then(data=>{
    console.log(data);  //then2
});
複製程式碼

2.4 示例

function loadImage(url){
    return new Promise(function(resolve,reject){
        let img = new Image();
        img.src = url;
        img.onload = function(){
            resolve();
        }
        img.onerror = function(){
            reject();
        }
    });
}

let img1 = loadImage("http://storage.xuetangx.com/public_assets/xuetangx/yifang/banner03.jpg");
img1.then(function(data){
    console.log("success");
},function(err){
    console.log("error");
});
複製程式碼

3.Promise進階

reject函式的引數一般是一個Error物件的例項,而resolve函式的引數除了是正常return返回的值之外,還有可能是一個Promise例項。
let p1 = new Promise(function(resolve,reject){
    setTimeout(()=>{
        resolve('hello');
    },3000);
});
let p2 = new Promise(function(resolve,reject){
    setTimeout(()=>{
        resolve(p1);
    },10);
});
p2.then((data)=>{
    console.log(data);
});
複製程式碼
p2會等待p1的執行結果,然後再執行,輸出的結果是hello,從輸出的結果可知,p1完成狀態轉變之後,傳遞給resolve(或者reject)的結果會傳遞給p2中的resolve。

4.catch()錯誤處理

catch()方法是Promise原型上的方法,用於指定發生錯誤時的回撥。
let p = new Promise(function(resolve,reject){
    setTimeout(()=>{
        resolve('p1');
    },0);
});
p.then((data)=>{
    console.log(data);
    throw new Error('then1');
    return 'then1';
}).then((data)=>{
    console.log(data);
    throw new Error('then2');
    return 'then2';
}).catch(err=>{     //在此處可以捕獲前面的錯誤
    console.log(err);   
});
複製程式碼
執行結果是:
p1
then1
可以看出,在第一個then中丟擲了一個錯誤,在最後一個Promise物件中可以catch到這個錯誤,所以,一般我們就不用在then方法中指定reject狀態的回撥函式了,而是指定catch方法即可。

5.all()方法

Promise的all方法,提供了並行執行非同步操作的能力,並且在所有非同步操作執行完後才執行回撥。
// task1
function task1(){
    console.log("task1");
    let p = new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log('set-01');
            resolve('then1');
        },1000);
    });
    return p;
}
// task2
function task2(){
    console.log('task2');
    let p = new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log('set-02');
            resolve('then2');
        },1000);
    });
    return p;
}
Promise.all([task1(),task2()]).then((data)=>{
    console.log('done');
    console.log(data);
});
複製程式碼
執行結果是:
task1
task2
set-01
set-02
done
['then1','then2']

6. race()方法

race()從字面意思就知道是賽跑的意思,其用法和all()方法相同,但是二者的區別是all()方法是全部的非同步操作都執行完畢後,才執行then回撥;而race()方法是隻要有一個非同步操作執行完畢後,就立即執行then回撥,需要注意的是,其它沒有執行完畢的非同步操作仍然會繼續執行,而不是停止了。

相關文章