1. 程式人生 > >pomise整理,boom!boom!boom!

pomise整理,boom!boom!boom!

promise:設計目的,為了解決非同步操作。

promise兩個特點

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

               ( promise三種狀態:1.pending(進行中)、fulfilled(已成功)和rejected(已失敗))     

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

               只有兩種可能:從pending變為fulfilled和從pending變為rejected。只要這兩種情況發生,狀態就凝固了,不會                 再變 了,會一直保持這個結果,這時就稱為 resolved(已定型)

簡單的promise:(這裡推薦使用catch所以就不寫then第二個引數的形式了)

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 非同步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
promise.then(function(value) {
  // success
}).catch(function(error) {
  // failure
});

resolve函式的作用是,將Promise物件的狀態從“未完成”變為“成功”,在非同步操作成功時呼叫,並將非同步操作的結果,作為引數傳遞出去;由promise.then接收resolve傳遞過來的引數

reject函式的作用是,將Promise物件的狀態從“未完成”變為“失敗”,在非同步操作失敗時呼叫,並將非同步操作報出的錯誤,作為引數傳遞出去。由promise.catch接收reject傳遞過來的引數

注意:

呼叫resolvereject並不會終結 Promise 的引數函式的執行。

new Promise((resolve, reject) => {
  resolve(1);
  console.log(2);
}).then(r => {
  console.log(r);
});
// 2
// 1

上面程式碼中,呼叫resolve(1)以後,後面的console.log(2)還是會執行,並且會首先打印出來。這是因為立即 resolved 的 Promise 是在本輪事件迴圈的末尾執行,總是晚於本輪迴圈的同步任務。

 

一般來說,呼叫resolvereject以後,Promise 的使命就完成了,後繼操作應該放到then方法裡面,而不應該直接寫在resolvereject的後面。所以,最好在它們前面加上return語句,這樣就不會有意外。

new Promise((resolve, reject) => {
  return resolve(1);
  // 後面的語句不會執行
  console.log(2);
})

Promise.prototype.then() === Promise.then()

then方法返回的是一個新的Promise例項(注意,不是原來那個Promise例項)。因此可以採用鏈式寫法,即then方法後面再呼叫另一個then方法。第一個回撥函式完成以後,會將返回結果作為引數,傳入第二個回撥函式。

promise(function(resolve, reject) {resolve('ok')})
.then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});

Promise.prototype.catch() === Promise.catch()

如果非同步操作丟擲錯誤,狀態就會變為rejected,就會呼叫catch方法指定的回撥函式,處理這個錯誤。另外,then方法指定的回撥函式,如果執行中丟擲錯誤,也會被catch方法捕獲。

例子:

const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.then(function(value){
    console.log(value) //這個log不會被呼叫
}).catch(function(error) {
    console.log(error);
});
// Error: test

如果 Promise 狀態已經變成resolved,再丟擲錯誤是無效的。

const promise = new Promise(function(resolve, reject) {
  resolve('ok');
  throw new Error('test');
});
promise
  .then(function(value) { console.log(value) })
  .catch(function(error) { console.log(error) });
// ok

Promise 物件的錯誤具有“冒泡”性質,會一直向後傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch語句捕獲。(getJSON()是一個封裝好的promise的ajax呼叫)

getJSON('/post/1.json').then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // 處理前面三個Promise產生的錯誤
});

上面程式碼中,一共有三個 Promise 物件:一個由getJSON產生,兩個由then產生。它們之中任何一個丟擲的錯誤,都會被最後一個catch捕獲。

catch方法返回的還是一個 Promise 物件,因此後面還可以接著呼叫then方法。

例:

const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行會報錯,因為x沒有宣告
    resolve(x + 2);
  });
};

someAsyncThing()
.catch(function(error) {
  console.log('oh no', error);
})
.then(function() {
  console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on

Promise.prototype.finally() 

finally方法用於指定不管 Promise 物件最後狀態如何,都會執行的操作。

例:

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

上面程式碼中,不管promise最後的狀態,在執行完thencatch指定的回撥函式以後,都會執行finally方法指定的回撥函式。finally方法的回撥函式不接受任何引數,這意味著沒有辦法知道,前面的 Promise 狀態到底是fulfilled還是rejected。這表明,finally方法裡面的操作,應該是與狀態無關的,不依賴於 Promise 的執行結果。

Promise.all()

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

例:

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

p的狀態由p1p2p3決定,分成兩種情況。

(1)只有p1p2p3的狀態都變成fulfilled(成功狀態)p的狀態才會變成fulfilled(成功狀態),此時p1p2p3的返回值組成一個數組,傳遞給p的回撥函式。

(2)只要p1p2p3之中有一個被rejected(失敗)p的狀態就變成rejected(失敗),此時第一個被reject的例項的返回值,會傳遞給p的回撥函式。

例:

// 生成一個Promise物件的陣列
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return getJSON('/post/' + id + ".json");
});

Promise.all(promises).then(function (posts) {
  // ...
}).catch(function(reason){
  // ...
});

上面程式碼中,promises是包含 6 個 Promise 例項的陣列,只有這 6 個例項的狀態都變成fulfilled,或者其中有一個變為rejected,才會呼叫Promise.all方法後面的回撥函式(then和catch)。

注意:

如果作為引數的 Promise 例項,自己定義了catch方法,那麼它一旦被rejected,並不會觸發Promise.all()catch方法。

Promise.resolve()

有時需要將現有物件轉為 Promise 物件,Promise.resolve方法就起到這個作用。

Promise.resolve方法的引數分成四種情況。

(1)引數是一個 Promise 例項

如果引數是 Promise 例項,那麼Promise.resolve將不做任何修改、原封不動地返回這個例項。

(2)引數是一個thenable物件

thenable物件指的是具有then方法的物件,比如下面這個物件。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

(3)引數不是具有then方法的物件,或根本就不是物件

如果引數是一個原始值,或者是一個不具有then方法的物件,則Promise.resolve方法返回一個新的 Promise 物件,狀態為resolved

(4)不帶有任何引數

Promise.resolve方法允許呼叫時不帶引數,直接返回一個resolved狀態的 Promise 物件。

所以,如果希望得到一個 Promise 物件,比較方便的方法就是直接呼叫Promise.resolve方法。

本來想著整理到這裡就結束了,感覺基本常用的方法就這些了,但是去了某大公司面試的時候,人家還是問了全部的方法,本著程式設計師縝密的思想還是多整理整理吧,(手動滑稽~)

Promise.race() 

Promise.race方法同Promise.all一樣,但狀態改變有些差距

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

上面程式碼中,只要p1p2p3之中有一個例項率先改變狀態,p的狀態就跟著改變。那個率先改變的 Promise 例項的返回值,就傳遞給p的回撥函式。

Promise.reject()

Promise.reject(reason)方法也會返回一個新的 Promise 例項,該例項的狀態為rejected(執行失敗)

const p = Promise.reject('出錯了');

最後帶來應用:

載入圖片

我們可以將圖片的載入寫成一個Promise,一旦載入完成,Promise的狀態就發生變化。

const preloadImage = function (path) {
  return new Promise(function (resolve, reject) {
    const image = new Image();
    image.onload  = resolve;
    image.onerror = reject;
    image.src = path;
  });
};