1. 程式人生 > >Promise物件用法簡介

Promise物件用法簡介

原自 阮一峰老師的 ECMAScript 6 入門

1.什麼是promise物件

  1. Promise是非同步程式設計的一種解決方案,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise物件。
  2. Promise物件可以獲取非同步操作的訊息,Promise提供統一的 API,各種非同步操作都可以用同樣的方法進行處理。

2.Promise物件的特點

  1. 物件的狀態不受外界影響。Promise物件代表一個非同步操作,有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。
  2. 一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise物件的狀態改變,只有兩種可能:從pending變為fulfilled和從pending變為rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱為 resolved(已定型)。如果改變已經發生了,你再對Promise物件添加回調函式,也會立即得到這個結果。

Promise物件,可以將非同步操作以同步操作的流程表達出來,避免層層巢狀的回撥函式。此外,Promise物件提供統一的介面,使得控制非同步操作更加容易。

Promise也有一些缺點:

首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消。 其次,如果不設定回撥函式,Promise

內部丟擲的錯誤,不會反應到外部。 第三,當處於pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。

3.基本用法

ES6 規定,Promise物件是一個建構函式,用來生成Promise例項。 下面程式碼創造了一個Promise例項。

const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 非同步操作成功 */){
    resolve(value);
  } else {
    reject(error); // 非同步操作失敗
  }
});

Promise建構函式接受一個函式作為引數,該函式的兩個引數分別是resolve

reject。它們是兩個函式,由 JavaScript 引擎提供,不用自己部署。

  • resolve函式的作用是,將Promise物件的狀態從“未完成”變為“成功”(即從 pending變為 resolved),在非同步操作成功時呼叫,並將非同步操作的結果,作為引數傳遞出去;
  • reject函式的作用是,將Promise物件的狀態從“未完成”變為“失敗”(即從 pending變為rejected),在非同步操作失敗時呼叫,並將非同步操作報出的錯誤,作為引數傳遞出去。

Promise例項生成以後,可以用then方法分別指定resolved狀態和rejected狀態的回撥函式。

promise.then(function (value) {
  // success
},function (error) {
  // failure
})

then方法可以接受兩個回撥函式作為引數。第一個回撥函式是Promise物件的狀態變為resolved時呼叫,第二個回撥函式是Promise物件的狀態變為rejected時呼叫。其中,第二個函式是可選的,不一定要提供。這兩個函式都接受Promise物件傳出的值作為引數。

下面是一個Promise物件的簡單例子。

function timeout (ms) {
  return new Promise ((resolve,reject) => {
    setTimeout(resolve,ms,'done');
  })
};
timeout(100).then((value) => {
  console.log(value);
});

上面程式碼中,timeout方法返回一個Promise例項,表示一段時間以後才會發生的結果。過了指定的時間(ms引數)以後,Promise例項的狀態變為resolved,就會觸發then方法繫結的回撥函式。

Promise新建後就會立即執行。

let promise = new Promise(function(resolve,regect){
  console.log('Promise');
  resolve();
});
promise.then(function () {
 console.log(resolve);
});
// Promise
// Hi!
// resolved

上面程式碼中,Promise新建後立即執行,所以首先輸出的是Promise。然後,then方法指定的回撥函式,將在當前指令碼所有同步任務執行完才會執行(因為javascript是單執行緒的),所以resolved最後輸出。

下面是非同步載入圖片的例子。

function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    const image = new Image();
    image.onload = function() {
      resolve(image);
    };
    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    };
    image.src = url;
  });
}

上面程式碼中,使用Promise包裝了一個圖片載入的非同步操作。如果載入成功,就呼叫resolve方法,否則就呼叫reject方法。

下面是一個用Promise物件實現的 Ajax 操作的例子。

const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();
  });
  return promise;
};
getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出錯了', error);
});

上面程式碼中,getJSON是對 XMLHttpRequest 物件的封裝,用於發出一個針對 JSON 資料的 HTTP 請求,並且返回一個Promise物件。需要注意的是,在getJSON內部,resolve函式和reject函式呼叫時,都帶有引數。

如果呼叫resolve函式和reject函式時帶有引數,那麼它們的引數會被傳遞給回撥函式。reject函式的引數通常是Error物件的例項,表示丟擲的錯誤;resolve函式的引數除了正常的值以外,還可能是另一個 Promise 例項,比如像下面這樣。

const p1 = new Promise(function (resolve, reject) {
  // ...
});
const p2 = new Promise(function (resolve, reject) {
  // ...
  resolve(p1);
})

上面程式碼中,p1p2都是 Promise 的例項,但是p2resolve方法將p1作為引數,即一個非同步操作的結果是返回另一個非同步操作。