JavaScript Promise 物件

admin發表於2019-04-24

JavaScript非同步操作在實際專案應用廣泛,否則一些當前司空見慣的操作就會將瀏覽器卡死。

舉兩個比較常見的JavaScript非同步操作,一個是ajax操作,另一個是FileReader讀取計算機檔案。

ajax讀取遠端伺服器資料和FileReader讀取計算機檔案的時候,不會阻塞其他操作。

當資料或者檔案讀取完畢之後,然後通過一個回撥函式來執行相應的操作。

通過回撥函式來完成非同步操作是過去不二的選擇,但是有一個問題。

看如下程式碼片段:

[JavaScript] 純文字檢視 複製程式碼
fs.readFile(fileA, function (err, data) {
  fs.readFile(fileB, function (err, data) {
    //code
  });
});

上面是一個簡單的回撥函式應用的程式碼片段,非常的簡單,具有兩層巢狀。

但是存在一個問題,那麼就是回撥函式較多的時候,層層巢狀,程式碼刻度行變的非常差。

可以想象程式碼會成為什麼樣子,在橫向上會變得非常長,一定非常壯觀,會可以說亂花漸欲迷人眼。

上述過多層層巢狀的現象也可以被稱作"回撥地獄",本文將要介紹的Promise就是為了解決此問題。

一.Promise介紹:

此物件在ES2015被標準化,是一個全新的非同步解決方案,能夠很好的避免"回撥地獄"現象。

事實上在標準化之前,此物件已經在應用,下面對其特點簡單總結如下:

(1).Promise物件可以儲存非同步操作的結果。

(2).Promise非同步操作具有三種狀態,Pending、Resolved和Rejected。

(3).Promise物件狀態的改變可能存在兩種情況,Pending到Resolved或者Pending到Rejected。

(4).Promise物件的狀態一旦確定,那麼就無法改變,要麼是Resolved,要麼是Rejected。

特別說明:Pending表示等待狀態,Resolved表示處於完成狀態,Rejected處於未完成狀態。

首先通過簡單程式碼片段看一下演示一下Promise物件的應用:

[JavaScript] 純文字檢視 複製程式碼
let p = new Promise(function(resolve, reject) {
  if (條件是否成立){
    resolve(value);
  } else {
    reject(error);
  }
});

下面對上述程式碼進行一下簡單分析:

(1).通過建構函式可以建立一個Promise物件例項,建構函式的引數是一個回撥函式。

(2).建構函式的回撥函式具有兩個函式引數,由引擎提供,也就是不需要我們提供。

(3).執行resolve函式,那麼狀態變為Resolved,執行reject函式,狀態變為Rejected。

(4).建構函式呼叫後,回撥函式會立馬執行,然後根據呼叫的是resolve還是reject函式,確定狀態。

(5).狀態確定後,再利用then方法執行對應的操作。下面再來看一段程式碼片段:

[JavaScript] 純文字檢視 複製程式碼
p.then(function(value) {
  //code
}, function(value) {
  //code
});

程式碼分析如下:

(1).then方法的引數是兩個回撥函式。

(2).如果p處於Resolved完成狀態,那麼執行第一個回撥函式,如果處於Rejected狀態。

(3).回撥函式中的引數value,分別是傳遞給resolve和reject函式的引數。

(3).第二回撥函式是可以省略的。

[JavaScript] 純文字檢視 複製程式碼執行程式碼
let p = new Promise(function (resolve, reject) {
    resolve("螞蟻部落");
})
p.then(function (val) {
    console.log(val) 
})

程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/24/222819ip6638lxnhq8omax.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

程式碼簡單分析如下:

(1).執行建構函式Promise,立馬執行回撥函式,也就會立馬呼叫resolve()函式,引數是"螞蟻部落"。

(2).於是p的狀態變為Resolved,然後執行then函式,進而執行其第一個回撥函式,最後列印"螞蟻部落"。

[JavaScript] 純文字檢視 複製程式碼執行程式碼
let p = new Promise(function (resolve, reject) {
  console.log("螞蟻部落一");
  resolve("螞蟻部落二");
})
p.then(function (val) { 
  console.log(val) 
})
console.log("螞蟻部落三");

程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/24/222853f0y8j38n8xs35n7f.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

程式碼分析如下:

(1).執行Promise建構函式之後,立馬執行其回撥函式,於是"螞蟻部落一"被列印。

(2).然後執行resolve("螞蟻部落二"),p的狀態變為Resolved,於是執行then方法,列印"螞蟻部落二"

(3).最後再列印"螞蟻部落三",然而事實是首先列印"螞蟻部落三",再列印"螞蟻部落二"。

(4).即便呼叫resolve("螞蟻部落二")不存在任何延遲,甚至可以認為是同步進行,但整體依然是非同步操作。

看如下程式碼例項:

[JavaScript] 純文字檢視 複製程式碼執行程式碼
setTimeout(function(){
  console.log(index);
},0); 
console.log("螞蟻部落");

程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/24/222922woxql7l9xoye3p8v.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

雖然延遲實現為0,但是setTimeout是非同步操作,所以首先列印'螞蟻部落二"。

更多內容可以參閱如下兩篇文章:

(1).JavaScript 非同步操作原理一章節。

(2).setTimeout(func,0)第二個引數為0分析一章節。

[JavaScript] 純文字檢視 複製程式碼
let P1 = new Promise(function(resolve, reject){
  //code
});
let P2 = new Promise(function (resolve, reject) {
  //code
  resolve(P1);
})

程式碼片段分析如下:

(1).傳遞給resolve和reject函式除了普通資料之外,可以是Promise物件例項。

(2).上述程式碼中,P2的狀態由P1的狀態決定,非常簡單,就不通過完整程式碼例項演示。

上面已經對Promise物件做了比較詳細的總體介紹,此物件還有其他涉及的方法。

考慮到文章的篇幅問題,本文不再做介紹,下面會給出對應文章的連結,可以自行參閱。

如果對本文有任何意見或者建議,可以在文章底部留言,本站會第一時間回覆。

二.相關閱讀:

(1).Promise.prototype.then() 方法一章節。

(2).Promise.prototype.catch()方法一章節。

(3).Promise.all()方法一章節。

(4).Promise.race()方法一章節。

(5).Promise.resolve()方法一章節。

(6).Promise.reject()方法一章節。

相關文章