ES6中Promise的用法及resolve、rejected、catch、finally說明


Promise是異步編程的一種解決方案。避免了相似於$.ajax()這種多個異步操做層層嵌套的問題。

Promise對象的特色:

  1. Promise對象有三種狀態:padding(初始狀態)fulfilled(異步成功以後的狀態)rejected(異步失敗的狀態),改變狀態的方式只有一種即異步的結果:若是成功狀態由padding——>fulfilled;不然狀態由padding——>rejected。沒法提供其餘方式改變狀態。
  2. 狀態一旦改變就沒法更改。
  3. 沒法取消Promise,一旦創建就會執行。。。

Promise語法

new Promise((resolve, reject) => {
    if (異步成功後) {
        resolve(value)//將Promise的狀態由padding改成fulfilled
    } else {
        reject(error)//將Promise的狀態由padding改成rejected
    }
}).then(value => {
    // resolve回調 
}, error => {
    // reject回調
})

上面的代碼表示若是異步成功後就會調用.then()裏面的第一個參數方法,不然就會調用.then()裏面的第二個參數方法。若是調用resolvereject有參數則會將參數分別傳遞給回調函數(.then裏面的第一個和第二個參數方法)。javascript

resolve的參數除了正常值外還能夠是一個Promise實例。如:java

let p1 = new Promise((resolve, reject) => {
    // ...
})
let p2 = new Promise((resolve, reject) => {
    // ...
    resolve(p1)
})

上面的代碼中p1p2爲Promise實例。p2resolvep1做爲參數。即:一個異步操做的結果是返回另外一個異步操做。
注意:這時p1的狀態決定了p2的狀態。若是p1的狀態爲padding那麼p2的回調會等p1的狀態改變後執行(即p1成功或失敗);若是p1的狀態爲resolvedrejected那麼p2的回調會當即執行。
示例:web

let p1 = new Promise((resolve, reject) => {
    setTimeout(()=>{
        reject(new Error('發生錯誤嘹'))
    },3000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(()=>{
        resolve(p1)
    },1000)
}).then(value => {
    console.log(value)
}).catch(error=>{
    console.log(error)
})

正由於如此上面代碼p2最終執行的是catch()方法,並不會走then()方法。ajax


調用resolvereject並不會終結 Promise 的參數函數的執行。
代碼示意:編程

new Promise((resolve, reject) => {
    setTimeout(()=>{
        resolve('then')
        console.log('promise')
    },2000)
}).then(value => {
    console.log(value)
})
//執行結果
//promise
//then

通常來講上面的console.log('promise')若是要執行放到回調函數更加規範json

Promise.then()

.then()的做用是爲 Promise 實例添加狀態改變時的回調函數
.then()方法的第一個參數是resolved狀態的回調函數,第二個參數(可選)是rejected狀態的回調函數。它的返回的是一個新的Promise實例。所以能夠採用鏈式寫法。數組

getJSON("/post/1.json").then(
  post => getJSON(post.commentURL)
).then(
  comments => console.log("resolved: ", comments),
  err => console.log("rejected: ", err)
)

上面代碼中,第一個then方法指定的回調函數,返回的是另外一個Promise對象。這時,第二個then方法指定的回調函數,就會等待這個新的Promise對象狀態發生變化。若是變爲resolved,就調用funcA,若是狀態變爲rejected,就調用funcBpromise

Promise.catch()

用於指定發生錯誤時的回調函數,最好是在.then()的鏈式調用最後調用一下此方法來捕獲異常。它的返回值仍然是個Promise對象異步

new Promise((resolve, reject) => {
    throw new Error('出現錯誤了')
}).then(value => {

}).catch(error=>{
    console.log(error)
})

上面代碼中若是Promise對象狀態變爲resolved,則會調用then方法指定的回調函數;若是異步操做拋出錯誤,狀態就會變爲rejected,就會調用catch方法指定的回調函數,處理這個錯誤。另外,then方法指定的回調函數,若是運行中拋出錯誤,也會被catch方法捕獲。svg


若是 Promise 狀態已經變成resolved,再拋出錯誤是無效的。

new Promise((resolve, reject) => {
    resolve('data')
    throw new Error('出現錯誤了')
}).then(value => {
    console.log(value)
}).catch(error=>{
    console.log(error)
})

這時不會運行catch()裏面的方法


通常來講,不要在then方法裏面定義 Reject 狀態的回調函數(即then的第二個參數),老是使用catch方法。

若是沒有使用catch方法指定錯誤處理的回調函數,Promise 對象即使拋出錯誤也不會影響到其它代碼

Promise.finally()

finally方法用於指定無論 Promise 對象最後狀態如何,都會執行的操做。該方法是 ES2018 引入標準的。
finally方法的回調函數不接受任何參數,這意味着沒有辦法知道,前面的 Promise 狀態究竟是fulfilled仍是rejected。這代表,finally方法裏面的操做,應該是與狀態無關的,不依賴於 Promise 的執行結果。
finally本質上是then方法的特例。以下代碼是等效的:

promise
.finally(() => {
  // 語句
});
// 等同於
promise
.then(
  result => {
    // 語句
    return result;
  },
  error => {
    // 語句
    throw error;
  }
);

Promise.all()

Promise.all方法用於將多個 Promise 實例,包裝成一個新的 Promise 實例

const p = Promise.all([p1, p2, p3])
  1. p1p2p3都爲Promise實例,若是不是調用Promise.resolve將它們轉成Promise實例。
  2. p的狀態由p1p2p3共同決定若是它們的狀態全都fulfilledp的狀態變成fulfilled,此時p1p2p3的返回值組成一個數組傳遞給p的回調
  3. p1p2p3中只要有一個爲rejected,則p的狀態變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。
  4. p1p2p3本身定義了catch方法,那麼它一旦被rejected,並不會觸發Promise.all()的catch方法
const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result)
.catch(e => e);

const p2 = new Promise((resolve, reject) => {
  throw new Error('報錯了');
})
.then(result => result)
.catch(e => e);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 報錯了]

上面代碼中,p1resolvedp2首先會rejected,可是p2有本身的catch方法,該方法返回的是一個新的 Promise 實例,p2指向的其實是這個實例。該實例執行完catch方法後,也會變成resolved,致使Promise.all()方法參數裏面的兩個實例都會resolved,所以會調用then方法指定的回調函數,而不會調用catch方法指定的回調函數。若是p2沒有本身的catch方法,就會調用Promise.all()catch方法。

Promise.race()

此方法和Promise.all()同樣接收多個Promise實例,返回一個新的Promise實例。

const p = Promise.race([p1, p2, p3]);

Promise.all()不一樣之處是隻要p一、p二、p3中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數

Promise.resolve()

Promise.resolve()的做用是將現有對象轉換成Promise對象。
如下的寫法是等效的

Promise.resolve('foo')
// 等價於
new Promise(resolve => resolve('foo'))

Promise.resolve()參數的4種狀況

參數是一個 Promise 實例

參數是一個 Promise 實例,這種狀況Promise.resolve()什麼都不作

參數是一個thenable對象

參數是具備then方法的對象(thenable對象)Promise.resolve方法會將這個對象轉爲Promise對象,而後就當即執行thenable對象的then方法。例如:

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

上面代碼中,thenable對象的then方法執行後,對象p1的狀態就變爲resolved,從而當即執行最後那個then方法指定的回調函數,輸出 42

參數是普通對象或原始值

參數不是具備then方法的對象,或根本就不是對象,Promise.resolve返回一個狀態爲resolved的對象

const p = Promise.resolve('Hello');
// 由於p的狀態爲resolved因此.then()會當即執行
p.then(function (s){
 console.log(s)
});
// Hello

不帶任何參數

這種狀況直接返回狀態爲resolved的Promise對象。若是但願獲得一個 Promise 對象,比較方便的方法就是直接調用。
當即resolve的 Promise 對象,是在本輪「事件循環」(event loop)的結束時,而不是在下一輪「事件循環」的開始時。示例:

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve().then(function () {
  console.log('two');
});

console.log('one');
// one
// two
// three

上面代碼中,setTimeout(fn, 0)在下一輪「事件循環」開始時執行,Promise.resolve()在本輪「事件循環」結束時執行,console.log(‘one’)則是當即執行,所以最早輸出。

Promise.reject()

返回狀態爲rejected的Promise對象

如下兩種寫法等效

const p = Promise.reject('出錯了');
// 等同於
const p = new Promise((resolve, reject) => {
    reject('出錯了')
})

p.catch(error=>{
    console.log(error)
})
// 出錯了