Promise的基本用法


Promise是異步編程的一種解決方案,從語法上說,Promise是一個對象,從它可以獲取異步操作的消息。

Promise的基本用法

Promise構造函數接受一個函數作為參數,該函數的兩個參數分別是resolve和reject。它們是兩個函數,由JavaScript引擎提供。

  • resolve函數的作用是,將Promise對象的狀態從“未完成”變為“成功”(即從Pending變為Resolved),在異步操作成功時調用,並將異步操作的結果作為參數傳遞出去。
  • reject函數的作用是,將Promise對象的狀態從“未完成”變為“失敗”(即從Pending變為Rejected),在異步操作失敗時調用,並將異步操作報出的錯誤作為參數傳遞出去。
  • then方法可以接受兩個回調函數作為參數。第一個回調函數是Promise對象的狀態變為Resolved時調用,第二個回調函數是Promise對象的狀態變為 Reject時調用。
var promise = new Promise(
    //異步執行,Promise對象創建后會被立即執行
    function (resolve,reject) {
        //耗時很長的異步操作
  if('異步處理成功') {  
        resolve();    //數據處理成功時調用
  } else {
        reject();    //數據處理失敗時調用
    }
        
    }
)
//Promise實例生成以后,可以用then方法分別指定Resolved狀態和Reject狀態的回調函數。
promise.then(
    function A() {
        //數據處理成功后執行
    },
    function B() {
        //數據處理失敗后執行
    }
)

 

下面我們舉一個簡單的例子來模擬一下異步操作成功和異步操作失敗函數的運行過程。

 

console.log('starting');
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2秒后,我運行了");
        resolve('異步操作成功了');     //1
        //reject('異步操作失敗了');    //2
     //return 'hello';       //3
}, 2000) }).then(function (value) { console.log('異步操作成功后執行我:',value); }, function (value) { console.log('異步操作失敗后執行我:',value); } ) console.log('我也運行了'); // 上面的代碼中1處代碼的調用,輸出順序是: //starting //我也運行了
//2秒后,我運行了 // 異步操作成功后執行我: 異步操作成功了 // 上面的代碼中2處代碼的調用,輸出順序是: //starting //我也運行了
//2秒后,我運行了 // 異步操作失敗后后執行我: 異步操作失敗了


//上面的代碼中3處代碼的調用,輸出順序是:
//starting
//我也運行了
//2秒后,我運行了

知代碼3處的return 'hello' 語句在新建的new Promise對象中並沒有被當作參數返回給then()函數內.那么會不會返回給promise了呢?我們用一段代碼來測試一下

console.log('starting');
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2秒后,我運行了");
        resolve('異步操作成功了');     //1
        //reject('異步操作失敗了');    //2
        return 'hello';
    }, 2000) 
    
})
promise.then(function (value) { 
    console.log('異步操作成功后執行我:',value);
},
function (value) {
    console.log('異步操作失敗后執行我:',value);
}
)
console.log('我也運行了');
console.log(promise);
setTimeout(function () {
    console.log('5秒后,我執行了');
    console.log(promise);
},5000);


//starting
//我也運行了
//Promise { pending }
  //[[PromiseStatus]]:"pending"
  //[[PromiseValue]]:undefined
  //__proto__:Promise {constructor: , then: , catch: , …}
//2秒后,我運行了
//異步操作成功后執行我: 異步操作成功了
//5秒后,我執行了
//Promise { resolved }
  //[[PromiseStatus]]:"resolved"
  //[[PromiseValue]]:"異步操作成功了"
  //__proto__:Promise {constructor: , then: , catch: , …}

由執行結果可知,變量promise仍然是new Promise對象的一個實例。所以return語句雖然被執行了,但對promise實例不會產生任何影響,相當於不存在。

由上面測試的代碼可知,Promise對象有以下兩個特點。
  (1)對象的狀態不受外界影響。Promise對象代表一個異步操作,有三種狀態:Pending(進行中)、Resolved(已完成,又稱Fulfilled) 和Rejected(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態,

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

resolve(value) VS resolve(Promise)

我們會在異步操作成功時調用resolve函數,其作用是將Promise對象的狀態從Pending變為Resolved,並將異步操作的結果作為參數傳遞給then()方法里的第一個函數的形參。

那么傳入的參數是值和傳入的參數是promise對象時有什么不同呢。我們來看一下例子。

當傳入的參數為值時:

var time = new Date();
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("1秒后,我運行了");
        resolve('異步操作成功了');     //1
    }, 2000) 
    
}).then(function (value) {
    console.log(value,new Date() - time);
})
//執行的輸出結果為:
//2秒后,我運行了
//異步操作成功了 1002

大約過了一秒左右,我們可以看到在resolved狀態的回調方法中,我們打印出了上面注釋中的內容。我們能夠通過resolve方法傳遞操作的結果,然后在回調方法中使用這些結果。

如果我們在resolve中傳入一個Promise實例呢?

var time = new Date();
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2秒后,我運行了");
        resolve('異步操作成功了');     //1
    }, 2000) 
    
})

var promise2 = new Promise(function (resolve,reject) {
    setTimeout(resolve,1000,promise);
}).then(function (value) {
    console.log(value,new Date() - time);
})

//執行后輸出的結果為:
//2秒后,我運行了 //異步操作成功了 2003

promise2經過了2秒后才打印出來結果。奇怪了,我們不是設置promise2經過1秒后執行嗎?

簡單說就是因為promise2中的resolve()函數傳入了promise對象,此時promise對象的狀態決定了promise的狀態,同時會把返回值傳給promise。

Promise/A+中規定 [[Resolve]](promise, x)

2.3.2.如果x是一個promise實例, 則以x的狀態作為promise的狀態

  2.3.2.1.如果x的狀態為pending, 那么promise的狀態也為pending, 直到x的狀態變化而變化。

  2.3.2.2.如果x的狀態為fulfilled, promise的狀態也為fulfilled, 並且以x的不可變值作為promise的不可變值。

  2.3.2.3.如果x的狀態為rejected, promise的狀態也為rejected, 並且以x的不可變原因作為promise的不可變原因。

2.3.4.如果x不是對象或函數,則將promise狀態轉換為fulfilled並且以x作為promise的不可變值。

 Promise.prototype.then()

Promise實例具有then方法,也就是說,then方法是定義在原型對象Promise.prototype上的。它的作用是為Promise實例添加狀態改變時的回調函數。 前面說過,then方法的第一個參數是Resolved狀態的回調函數,第二個參數(可選)是Rejected狀態的回調函數。

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

var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2秒后,我運行了");
        resolve('異步操作成功了');     //1
    }, 2000) 
    
})
promise.name = 'promise';
console.log('promise:',promise)
var promise2 = promise.then(function (value) {
    console.log(value);
})
promise2.name = 'promise2';
console.log('promise2:',promise2);

// promise:
//     Promise { pending }
//     [[PromiseStatus]]:"pending"
//     [[PromiseValue]]:undefined
//     name:"promise"
//     __proto__:Promise {constructor: , then: , catch: , …}
// promise2:
// Promise { pending }
//     [[PromiseStatus]]:"pending"
//     [[PromiseValue]]:undefined
//     name:"promise2"
//     __proto__:Promise {constructor: , then: , catch: , …}

 

我們可以知道promise.then()方法執行后返回的是一個新的Promise對象。也就是說上面代碼中的promise2是一個Promise對象,它的實現效果和下面的代碼是一樣的,只不過在then()方法里,JS引擎已經自動幫我們做了。

 

promise2 = new Promise(function (resolve,reject) {})

 

既然在then()函數里已經自動幫我實現了一個promise對象,但是我要怎么才能給resolve()或reject()函數傳參呢?其實在then()函數里,我們可以用return()的方式來給promise2的resolve()或reject()傳參。看一個例子。

//var time = new Date();
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2秒后,我運行了");
        resolve('異步操作成功了');     //1
        //reject('異步操作失敗了');    //2
    }, 2000) 
    
})
//console.log('promise:',promise)
var promise2 = promise.then(function (value) {
    console.log(value);
    //resolve('nihao');  //報錯。注意,這里不能寫resolve()方法,因為在then函數里是沒有resolve方法的
    return (1);       
    //return promise;   //也可以return一個promise對象,返回promise對象執行后resolve('參數值')或reject('參數值')內部的參數值
  //如果不寫return的話,默認返回的是return undefined 。 }) var promise3 = promise2.then(function (value) { console.log('is:',value); },function (value) { console.log('error:',value); }) promise2.name = 'promise2'; setTimeout(function () { console.log(promise2); },3000); //2秒后,我運行了 //異步操作成功了 //is: 1 //Promise {resolved} //name:"promise2" //__proto__:Promise //[[PromiseStatus]]:"resolved" //[[PromiseValue]]:1

Promise與錯誤狀態處理

.then(null, rejection),用於指定異步操作發生錯誤時執行的回調函數。下面我們做一個示例。

var promise = new Promise(function(resolve, reject) {
    setTimeout(function () {
            reject('error');
    },2000);
}).then(null,function(error) {
    console.log('rejected', error)
});
//rejected error

我們知道then()方法執行后返回的也是一個promise對象,因此也可以調用then()方法,但這樣的話為了捕獲異常信息,我們就需要為每一個then()方法綁定一個.then(null, rejection)。由於Promise對象的錯誤信息具有“冒泡”性質,錯誤會一直向后傳遞,直到被捕獲為止。因此Promise為我們提供了一個原型上的函數Promise.prototype.catch()來讓我們更方便的 捕獲到異常。

我們看一個例子

 

var promise = new Promise(function(resolve, reject) {
    setTimeout(function () {
            reject('error');
    },2000);
}).then(function(value) {
    console.log('resolve', value);
}).catch(function (error) {
    console.log(error);
})
//運行結果
//error

上面代碼中,一共有二個Promise對象:一個由promise產生,一個由then產生。它們之中任何一個拋出的錯誤,都會被最后一個catch捕獲。

但是如果用.then(null, rejection)方法來處理錯誤信息,我們需要在每一個rejection()方法中返回上一次異常信息的狀態,這樣當調用的then()方法一多的時候,對會對代碼的清晰性和邏輯性造成影響。

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


免責聲明!

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



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