1. 程式人生 > >PWA(Progressive Web App)入門系列:(四)Promise

PWA(Progressive Web App)入門系列:(四)Promise

前言

這一章說一下ES6的Promise物件。為什麼要在PWA系列的文章中講Promise呢?因為PWA中的許多技術API中都是以Promise返回的方式返回的,為了對後續章節中PWA技術API更好的理解,這裡就來說一個Promise物件。

Promise出現的背景

在JavaScript當中,處理非同步操作時,我們需要知道操作是否已經完成,當執行完成的時候會返回一個回撥函式,表示操作已經完成。所以在處理非同步操作時,通常是使用回撥巢狀的方式(CallBack)。但是如果出現多層回撥巢狀,也就是我們常說的回撥金字塔(Pyramid of Doom),絕對是一種糟糕的程式設計體驗。

像這樣:

function a1() {
  function a2() {
    function a3() {
      function a4() {
        function a5() {
          ...
        }
      }
    }
  }
}

回撥方式主要會導致兩個關鍵問題:

  1. 巢狀太深程式碼可讀性太差
  2. 行邏輯必須序列執行。

在這種情況下,Promise 物件出現了。2015 年 6 月,加入了ECMAScript 6 的標準。

Promise簡介

Promise 物件用於一個非同步操作的最終完成(或失敗)及其結果值的表示。一個 Promise 物件代表一個目前還不可用,但是在未來的某個時間點可以被解析的值。它允許你以一種同步的方式編寫非同步程式碼。Promises 將巢狀的回撥改造成一系列的.then

的鏈式呼叫,去除了層層巢狀的劣式程式碼風格。Promises 不是一種解決具體問題的演算法,而已一種更好的程式碼組織模式。

上面的程式碼,用Promise的方式可以寫成:

a1.then(function(data) {
  return a2(data)
})
.then(function(data) {
  return a3(data)
})
.then(function(data) {
  return a4(data)
})
.then(function(data) {
  return a5(data)
})
...

Promise 物件有以下兩個特點

  1. 物件的狀態不受外界影響。 Promise 物件代表一個非同步操作,有三種狀態:Pending(進行中)、Resolved(已完成,又稱 Fulfilled)、 Rejected(已失敗)。根據非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是 Promise 這個名字的由來,它的英語意思就是“承諾”,表示無法通過其他手段改變。
  2. 一旦狀態改變,就不會再變,任何時候都可以得到這個結果。 Promise 物件的狀態改變,只有兩種可能:從 Pending 變為 Resolved 和從 Pending 變為 Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對 Promise 物件添加回調函式,也會立即得到這個結果。

Promise狀態:



語法

new Promise( function(resolve, reject) {...} /* executor */  );

executor是一個帶有 resolvereject 兩個引數的函式 。executor 函式在Promise建構函式執行時同步執行,被傳遞 resolvereject 函式(executor 函式在Promise建構函式返回新建物件前被呼叫)。resolvereject 函式被呼叫時,分別將promise的狀態改為fulfilled(完成)或rejected(失敗)。executor 內部通常會執行一些非同步操作,一旦完成,可以呼叫resolve函式來將promise狀態改成fulfilled,或者在發生錯誤時將它的狀態改為rejected。
如果在executor函式中丟擲一個錯誤,那麼該promise 狀態為rejected。executor函式的返回值被忽略。

基本用法

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

  if (/* 操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

注意:例項化的Promise物件會立即執行



方法

下面說一下Promise物件的方法。

Promise.prototype.then()

then方法是定義在原型物件Promise.prototype上的。它最多需要有兩個引數:Promise 的成功和失敗情況的回撥函式。then方法的第一個引數是resolved狀態的回撥函式,第二個引數(可選)是rejected狀態的回撥函式。

then方法返回的是一個新的Promise例項。因此可以採用鏈式寫法,即then方法後面再呼叫另一個then方法。

Promise.prototype.catch()

catch() 方法返回一個Promise,只處理拒絕的情況。它的行為與呼叫Promise.prototype.then(undefined, onRejected) 相同。用於指定發生錯誤時的回撥函式。

taskkA()
.then(function() {
  return taskB()
})
.then(function() {
  return taskC()
})
.catch(function(err) {
  // ...
})
.then(function() {
  return taskD()
})



Promise.resolve()

返回一個狀態由給定value決定的Promise物件。如果該值是一個Promise物件,則直接返回該物件;如果該值是thenable(即,帶有then方法的物件),返回的Promise物件的最終狀態由then方法執行決定;否則的話(該value為空,基本型別或者不帶then方法的物件),返回的Promise物件狀態為fulfilled,並且將該value傳遞給對應的then方法。通常而言,如果你不知道一個值是否是Promise物件,使用Promise.resolve(value) 來返回一個Promise物件,這樣就能將該value以Promise物件形式使用。

Promise.resolve(value)
Promise.resolve(promise)
Promise.resolve(thenable)

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

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

Promise.reject()

返回一個狀態為失敗的Promise物件,並將給定的失敗資訊傳遞給對應的處理方法。該例項的狀態為rejected。

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

p.catch(function(err) {
  console.log(err)
})
// 出錯了

Promise.all()

這個方法返回一個新的promise物件,該promise物件在iterable引數物件裡所有的promise物件都成功的時候才會觸發成功,一旦有任何一個iterable裡面的promise物件失敗則立即觸發該promise物件的失敗。這個新的promise物件在觸發成功狀態以後,會把一個包含iterable裡所有promise返回值的陣列作為成功回撥的返回值,順序跟iterable的順序保持一致;如果這個新的promise物件觸發了失敗狀態,它會把iterable裡第一個觸發失敗的promise物件的錯誤資訊作為它的失敗錯誤資訊。Promise.all方法常被用於處理多個promise物件的狀態集合。

Promise.all(iterable)

iterable: 一個可迭代物件,例如一個 Array 或 String。

上述可迭代物件中的所有 Promise 被 resolve 之後返回 resolve,或者在任一 Promise 被 reject 後返回 reject。

Promise.race()

當iterable引數裡的任意一個子promise被成功或失敗後,父promise馬上也會用子promise的成功返回值或失敗詳情作為引數呼叫父promise繫結的相應控制代碼,並返回該promise物件。

用法和Promise.all()類似。

Promise.race(iterable);

基本應用

下面根據Promise的特性,做幾個例子。

非同步載入圖片

function loadImage(url) {
  return new Promise(function(resolve, reject) {
    var img= new Image();

    img.onload = function() {
      resolve(img);
    };

    img.onerror = function(err) {
      reject(new Error(err));
    };

    img.src = url;
  });
}

網路請求超時處理

Promise.race([
  fetch('http://xxxx.xxx'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('請求超時')), 6000)
  })
]).
then(function(data) {
  // ...
})
.catch(function(err) {
  // 處理錯誤...
})

總結

這一篇裡,對Promise的背景由來,及相關方法進行了相應的介紹說明,也瞭解到了Promise在非同步處理上的使用優勢。


部落格名稱:王樂平部落格

CSDN部落格地址:http://blog.csdn.net/lecepin

知識共享許可協議
本作品採用 知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。