Promise是異步編程的一種解決方案,從語法上說,Promise是一個對象,從它能夠獲取異步操做的消息。編程
Promise構造函數接受一個函數做爲參數,該函數的兩個參數分別是resolve和reject。它們是兩個函數,由JavaScript引擎提供。promise
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函數,其做用是將Promise對象的狀態從Pending變爲Resolved,並將異步操做的結果做爲參數傳遞給then()方法裏的第一個函數的形參。spa
那麼傳入的參數是值和傳入的參數是promise對象時有什麼不一樣呢。咱們來看一下例子。prototype
當傳入的參數爲值時:code
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);//setTimeout和setInterval中的第三個、及之後的參數會做爲第一個參數func的參數傳遞給func函數 }).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實例具備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
.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對象的錯誤信息具備「冒泡」性質,錯誤會一直向後傳遞,直到被捕獲爲止(向後傳遞指的是:後面的then函數會一直執行rejected下的代碼,並拋出第一個then函數/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方法。