聊一聊Javascript中的Promise物件

廣州蘆葦科技Java開發團隊發表於2018-12-12

是什麼

先直接上圖,列印一下Promise物件,觀察下Promise是什麼

1console.dir(Promise)
複製程式碼
Promise物件
Promise物件


可以知道,Promise是一個建構函式,有著reject、resolve函式。prototype有then、catch等方法,說明了只要是Promise物件都會有這兩個方法。

Promise建構函式是傳入一個函式

怎麼用

1var promise = new Promise((resolve, reject) => {
2    setTimeout(function () {
3        console.log('執行完成');
4        resolve('隨便什麼資料');
5    }, 2000);
6});
7
8result:
9執行完成
複製程式碼

傳入的函式有兩個引數:resolve、reject,分別表示非同步操作執行成功後的回撥函式和非同步操作失敗後的回撥函式。其實這裡用“成功”和“失敗”來描述並不準確,按照標準來講,resolve是將Promise的狀態置為fullfiled,reject是將Promise的狀態置為rejected。不過在我們開始階段可以先這麼理解,後面再細究概念。

執行以上程式碼,發現promise並沒有呼叫,只是new了一個Promise,對應的傳入函式的程式碼就已經執行了。因此,我們通常都是把Promise包在一個函式中,在需要的時候才去執行這個函式,如:

 1function f() {
 2    var promise = new Promise((resolve, reject) => {
 3        setTimeout(function () {
 4            console.log('執行完成');
 5            resolve('隨便什麼資料');
 6        }, 2000);
 7    });
 8    return promise
 9}
10
11f()
12
13result:
14執行完成
複製程式碼

這時引出兩個問題。為什麼需要大費周章的包裝這樣的一個函式?resolve有什麼用?

為什麼需要引入

1f().then(function(data){
2    console.log(data);
3    //後面可以用傳過來的資料做些其他操作
4    //......
5});
複製程式碼

在f()的返回上可以直接呼叫then方法,then接收一個引數,是函式,並且會拿到我們在f中呼叫resolve時傳入的引數。執行這段程式碼,會在2秒後輸出“執行完成”,緊接著輸出“隨便什麼資料”。

這時,我們恍然大悟了,原來then呼叫的function相當於我們之前寫的回撥函式,相當於是這種形式:

 1function runAsync(callback){
 2    setTimeout(function(){
 3        console.log('執行完成');
 4        callback('隨便什麼資料');
 5    }, 2000);
 6}
 7
 8runAsync(function(data){
 9    console.log(data);
10});
複製程式碼

那使用Promise有什麼好處呢?我們不妨考慮一個問題,如果callback函式也是一個非同步操作,而且執行完後也需要有相應的回撥函式,可能會變成 runAsync(function(data,function(data){}){…..})。看起來十分醜陋,假如使用Promise程式碼就不會這麼醜陋了~

Promise的優勢在於,可以在then方法中繼續寫Promise物件並返回,然後繼續呼叫then來進行回撥操作。

鏈式操作的用法

所以,從表面上看,Promise只是能夠簡化層層回撥的寫法,而實質上,Promise的精髓是“狀態”,用維護狀態、傳遞狀態的方式來使得回撥函式能夠及時呼叫,它比傳遞callback函式要簡單、靈活的多。所以使用Promise的正確場景是這樣的:

 1runAsync1()
 2.then(function(data){
 3    console.log(data);
 4    return runAsync2();
 5})
 6.then(function(data){
 7    console.log(data);
 8    return runAsync3();
 9})
10.then(function(data){
11    console.log(data);
12});
13
14
15function runAsync1(){
16    var p = new Promise(function(resolve, reject){
17        //做一些非同步操作
18        setTimeout(function(){
19            console.log('非同步任務1執行完成');
20            resolve('隨便什麼資料1');
21        }, 1000);
22    });
23    return p;            
24}
25function runAsync2(){
26    var p = new Promise(function(resolve, reject){
27        //做一些非同步操作
28        setTimeout(function(){
29            console.log('非同步任務2執行完成');
30            resolve('隨便什麼資料2');
31        }, 2000);
32    });
33    return p;            
34}
35function runAsync3(){
36    var p = new Promise(function(resolve, reject){
37        //做一些非同步操作
38        setTimeout(function(){
39            console.log('非同步任務3執行完成');
40            resolve('隨便什麼資料3');
41        }, 2000);
42    });
43    return p;            
44}
45
46result:
47非同步任務1執行完成
48隨便什麼資料1
49非同步任務2執行完成
50隨便什麼資料2
51非同步任務3執行完成
52隨便什麼資料3
複製程式碼

我們可以通過鏈式程式設計避免了包含多函式回撥的情況,當然並不是都需要return Promise物件,你也可以返回其他資料型別,在後面的then中就能直接直接接收到資料了,比如:

 1runAsync1()
 2.then(function(data){
 3    console.log(data);
 4    return runAsync2();
 5})
 6.then(function(data){
 7    console.log(data);
 8    return '直接返回資料';  //這裡直接返回資料
 9})
10.then(function(data){
11    console.log(data);
12});
13
14result:
15非同步任務1執行完成
16隨便什麼資料1
17非同步任務2執行完成
18隨便什麼資料2
19直接返回資料
複製程式碼

reject用法

前面我們一直都是使用resolve函式,並沒有使用reject函式。resolve函式是表示了“執行成功”的回撥,reject函式表示“執行失敗”的函式。

 1function runAsync1() {
 2    var p = new Promise(function (resolve, reject) {
 3        //做一些非同步操作
 4        setTimeout(function () {
 5            console.log('非同步任務1執行完成');
 6            // resolve('隨便什麼資料1');
 7            reject("error")
 8        }, 1000);
 9    });
10    return p;
11}
12
13runAsync1().then((data) => {
14    console.log('resolve');
15    console.log(data)
16}, (error, data) => {
17    console.log('rejected');
18    console.log(error)
19})
20
21result:
22非同步任務1執行完成
23rejected
24error
複製程式碼

需要注意的是,只要執行reject或者resolve,後面的就不會執行了,如:

 1function runAsync1() {
 2    var p = new Promise(function (resolve, reject) {
 3        //做一些非同步操作
 4        setTimeout(function () {
 5            console.log('非同步任務1執行完成');
 6            resolve('隨便什麼資料1');
 7            reject("error")
 8        }, 1000);
 9    });
10    return p;
11}
複製程式碼

因為resolve在reject的前面,所以只會執行resolve,不會執行reject。

總結

  • Promise是一個物件
  • 引入是為了消除多重回撥函式
  • Promise還有很多方法沒有介紹(catch、finally、success、fail)

參考文獻

ECMAScript6入門
大白話講解Promise(一)
ECMAScript6入門


房清

廣州蘆葦科技Java開發團隊

蘆葦科技-廣州專業網際網路軟體服務公司

抓住每一處細節 ,創造每一個美好

關注我們的公眾號,瞭解更多

想和我們一起奮鬥嗎?lagou搜尋“ 蘆葦科技 ”或者投放簡歷到 server@talkmoney.cn 加入我們吧

關注我們,你的評論和點贊對我們最大的支援


相關文章