Promise 學習筆記


所謂Promise,簡單說就是一個容器,里面保存着某個未來才會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise是一個對象,從它可以獲取異步操作的消息。Promise提供統一的API,各種異步操作都可以用同樣的方法進行處理。

Promise對象有以下兩個特點。

(1)對象的狀態不受外界影響。Promise對象代表一個異步操作,有三種狀態:Pending(進行中)、Resolved(已完成,又稱Fulfilled)和Rejected(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。

(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從Pending變為Resolved和從Pending變為Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。

Promise也有一些缺點。首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消(發起 fetch 請求,不能 abort。fetch 是基於 Promise 設計)。其次,如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。第三,當處於Pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。

如果某些事件不斷地反復發生,一般來說,使用stream模式是比部署Promise更好的選擇。

var promise = new Promise(function(resolve, reject) {
  // 進行異步操作

  if (/* 異步操作成功 */){
    resolve(value);  // 調用 resolve,將異步操作返回的 value,作為參數傳遞出去
  } else {
    reject(error);  // 調用 reject,將異步操作返回的 error,作為參數傳遞出去
  }
});
promise.then(function(value) {
  // 接收 resolve 傳遞的參數,進行處理
}, function(error) {
  // 接收 reject 傳遞的參數,進行處理。第二個參數為可選的
});

在新建 Promise 的時候,new Promise 里的代碼會立即執行:

let promise = new Promise(function(resolve, reject) {
  // new Promise 新建了 Promise,這里面的代碼立即執行
  console.log('Promise');
  resolve();
});

// then 方法的回調函數是異步操作,所以會添加到事件循環的任務隊列中,主線程操作完畢后才執行
promise.then(function() {
  console.log('Resolved.');
});

console.log('Hi!');

// Promise
// Hi!
// Resolved

Promise 封裝 AJAX

var getJSON = function(url) {
  var promise = new Promise(function(resolve, reject){
    var client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

    function handler() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        // 使用 resolve 得到 AJAX 獲取的數據
        resolve(this.response);
      } else {
        // 使用 reject 拋出錯誤
        reject(new Error(this.statusText));
      }
    };
  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出錯了', error);
});

 

Promise.prototype.then() 

then方法返回的是一個新的Promise實例(注意,不是原來那個Promise實例)。因此可以采用鏈式寫法,即then方法后面再調用另一個then方法。

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {  // 第二個 then 方法中,函數接收的參數是前一個 then 方法中的返回值
  // ...
});
getJSON("/post/1.json").then(function(post) {
  // 在 then 方法中調用 getJSON 返回了另一個 Promise 對象
  return getJSON(post.commentURL);
}).then(function funcA(comments) {  // 這個 then 方法就會根據前一個 then 方法返回的 Promise 對象的狀態,發生變化后,調用 funcA 或 funcB
  console.log("Resolved: ", comments);
}, function funcB(err){
  console.log("Rejected: ", err);
});

Promise.prototype.catch() 

Promise.prototype.catch方法是.then(null, rejection)的別名,用於指定發生錯誤時的回調函數。

getJSON("/posts.json").then(function(posts) {
  // ...
}).catch(function(error) {
  // 處理 getJSON 和 前一個回調函數運行時發生的錯誤
  console.log('發生錯誤!', error);
});

Promise對象的錯誤具有“冒泡”性質,會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch語句捕獲。

一般來說,不要在then方法里面定義Reject狀態的回調函數(即then的第二個參數),總是使用catch方法。

需要注意的是,catch方法返回的還是一個Promise對象,因此后面還可以接着調用then方法。

var 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

上面代碼運行完catch方法指定的回調函數,會接着運行后面那個then方法指定的回調函數。如果沒有報錯,則會跳過catch方法。

 

Promise.all() 

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

var p = Promise.all([p1, p2, p3]);  // p1, p2, p3 都是 Promise 對象實例

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

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

(2)只要p1p2p3之中有一個被rejectedp的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。

// 生成一個Promise對象的數組
var promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return getJSON("/post/" + id + ".json");
});

Promise.all(promises).then(function (posts) {
  // promises 全部 resolved 的情況,走這里
}).catch(function(reason){
  // promises 中有 rejected 的情況,走這里
});

 

Promise.race()

var p = Promise.race([p1,p2,p3]);  // p1, p2, p3 都是 Promise 對象實例

只要p1p2p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的Promise實例的返回值,就傳遞給p的回調函數。

 

done()

asyncFunc()
  .then(f1)
  .catch(r1)
  .then(f2)
  .done(onFulfilled, onRejected);

done方法的使用,可以像then方法那樣用,提供FulfilledRejected狀態的回調函數,也可以不提供任何參數。但不管怎樣,done都會捕捉到任何可能出現的錯誤,並向全局拋出。

 

finally()

server.listen(0)
  .then(function () {
    // run test
  })
  .finally(server.stop);

finally方法用於指定不管Promise對象最后狀態如何,都會執行的操作。它與done方法的最大區別,它接受一個普通的回調函數作為參數,該函數不管怎樣都必須執行。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM