2020-09-04

阮一峰的ES6---Promise對(duì)象

Promise的含義

promise是異步編程的一種解決方法,比傳統(tǒng)的回調(diào)函數(shù)和事件更合理更強(qiáng)大
。他由社區(qū)最早提出和實(shí)現(xiàn),ES6將其寫(xiě)進(jìn)語(yǔ)言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了promise對(duì)象。
所謂promise,簡(jiǎn)單說(shuō)是一個(gè)容器,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果,從語(yǔ)法上說(shuō),promise是一個(gè)對(duì)象,從它可以獲取異步操作的消息,promise提供了統(tǒng)一的API,各種異步操作都可以用同樣的方法進(jìn)行處理。
promise對(duì)象的特點(diǎn)
(1)對(duì)象的狀態(tài)不受外界影響,promise對(duì)象代表一個(gè)異步操作,有三種狀態(tài),pending(進(jìn)行中)、fulfilled(已成功)、rejected(已失?。?。只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無(wú)法改變這個(gè)狀態(tài),這也是promise這個(gè)名字的由來(lái)“承若”;
(2)一旦狀態(tài)改變就不會(huì)再變,任何時(shí)候都可以得到這個(gè)結(jié)果,promise對(duì)象的狀態(tài)改變,只有兩種可能:從pending變?yōu)閒ulfilled,從pending變?yōu)閞ejected。這時(shí)就稱為resolved(已定型)。如果改變已經(jīng)發(fā)生了,你再對(duì)promise對(duì)象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果,這與事件(event)完全不同,事件的特點(diǎn)是:如果你錯(cuò)過(guò)了它,再去監(jiān)聽(tīng)是得不到結(jié)果的。

有了Promise對(duì)象,就可以將異步操作以同步操作的流程表達(dá)出來(lái),避免了層層嵌套的回調(diào)函數(shù)。此外,Promise對(duì)象提供統(tǒng)一的接口,使得控制異步操作更加容易。

Promise也有一些缺點(diǎn)。首先,無(wú)法取消Promise,一旦新建它就會(huì)立即執(zhí)行,無(wú)法中途取消。其次,如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯(cuò)誤,不會(huì)反應(yīng)到外部。第三,當(dāng)處于pending狀態(tài)時(shí),無(wú)法得知目前進(jìn)展到哪一個(gè)階段(剛剛開(kāi)始還是即將完成)。

用法

promise對(duì)象是一個(gè)構(gòu)造函數(shù),用來(lái)生成promise實(shí)例;

  • 創(chuàng)建一個(gè)promise對(duì)象實(shí)例
var promise = new Promise( function( resolve, reject) {
       //some code 
      if(//異步操作成功){
        resolve(value);
         }else{
         reject(error);
         }
});

Promise構(gòu)造函數(shù)接收一個(gè)函數(shù)作為參數(shù),該函數(shù)的兩個(gè)參數(shù)分別是resolve和reject,他們是兩個(gè)函數(shù),由Javascript引擎提供,不用自己部署。

resolve函數(shù)的作用是,將promise對(duì)象的狀態(tài)從“pending”變?yōu)椤痳esolved‘’,在異步操作成功時(shí)調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去;

reject函數(shù)的作用是,將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending 變?yōu)?rejected),在異步操作失敗時(shí)調(diào)用,并將異步操作報(bào)出的錯(cuò)誤,作為參數(shù)傳遞出去。

promise實(shí)例生成以后,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)

promise.then(
    function(value){
   //success
   },
    function(error){
   //failure
 });

then方法可以接受連個(gè)回調(diào)函數(shù)作為參數(shù),第一個(gè)回調(diào)函數(shù)是promise對(duì)象的狀態(tài)變?yōu)閞esolved時(shí)調(diào)用,第二個(gè)回調(diào)函數(shù)是promise對(duì)象的狀態(tài)變?yōu)閞ejected時(shí)調(diào)用,其中,第二個(gè)函數(shù)是可選的,不一定要提供,這兩個(gè)函數(shù)都接受promise對(duì)象傳出的值作為參數(shù);
promise對(duì)象的簡(jiǎn)單例子:

function timeOut (ms) {
      return new Promise(function(resolve,reject) {
                 return    setTimeout(resolve,ms,"done");
       })
}
timeOut(3000).then( function(value){
console.log(value);
})

timeOut方法返回一個(gè)promise實(shí)例,表示一段時(shí)間以后才會(huì)發(fā)生的結(jié)果,過(guò)了指定的時(shí)間(ms)以后,promise實(shí)例的狀態(tài)變?yōu)閞esolved,就會(huì)觸發(fā)then方法綁定的回調(diào)函數(shù)

  • Promise新建后就會(huì)立即執(zhí)行
let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});

promise.then(function() {
  console.log('resolved.');
});

console.log('Hi!');

// Promise
// Hi!
// resolved

Promise 新建后立即執(zhí)行,所以首先輸出的是Promise。然后,then方法指定的回調(diào)函數(shù),將在當(dāng)前腳本所有同步任務(wù)執(zhí)行完才會(huì)執(zhí)行,所以resolved最后輸出。

  • 異步加載圖片的例子
function loadImageAsync (url) {
      return new Promise( function( resolve, reject){
              var image = new Image();
             image .onload = function(){
                    resolve(image);
               };
             image.onerror = function () {
                     reject(new Error("could not load image at "+ url) );
             };
            image.src =url;
        });
}
loadImageAsync(url).then( function (value) {
        console.log(value);
})

使用Promise包裝了一個(gè)圖片加載的異步操作。如果加載成功,就調(diào)用resolve方法,否則就調(diào)用reject方法。

  • promise對(duì)象實(shí)現(xiàn)ajax操作的例子
var getJSON = function (url) {
     var promise = new Promise( function(resolve,reject ) {
           var XHR = new XMLHttpRequest();
           XHR.open("GET",url);
           XHR.onreadystatechange = function () {
           if (this.readyState !==4) {return;}
           if(this.status == 200) {
               resolve(this.response);
           } else{
               reject(new Error(this.statusText) );
            }
           };
            XHR.responseType = "json";
            XHR.setRequestHeader("Accept","application/json");
           XHR.send();
     });
return promise;
};
getJSON("posts.json").then(function(json){
       console.log("Contents : "+json );
         }, function(error) {
       console.log("出錯(cuò)了", error);
}) ;

getJSON是對(duì)XMLHTTPRequest對(duì)象的封裝,用于發(fā)出一個(gè)針對(duì)JSON數(shù)據(jù)的HTTP請(qǐng)求,并且返回一個(gè)promise對(duì)象,需要注意的是,在getJSON內(nèi)部,resolve函數(shù)和reject函數(shù)調(diào)用時(shí),都帶有參數(shù);
如果調(diào)用resolve函數(shù)和reject函數(shù)時(shí)帶有參數(shù),那么他們的參數(shù)會(huì)被傳遞給回調(diào)函數(shù),reject函數(shù)的參數(shù)通常是Error對(duì)象的實(shí)例,表示拋出的錯(cuò)誤,resolve函數(shù)的參數(shù)除了正常的值以外,還可以是另一個(gè)promise實(shí)例;

var p1 = new Promise(function (resolve, reject) {
  // ...
});

var p2 = new Promise(function (resolve, reject) {
  // ...
  resolve(p1);
})

p1和p2都是 Promise 的實(shí)例,但是p2的resolve方法將p1作為參數(shù),即一個(gè)異步操作的結(jié)果是返回另一個(gè)異步操作。
這時(shí)p1的狀態(tài)就會(huì)傳遞給p2,也就是說(shuō),p1的狀態(tài)決定了p2的狀態(tài)。如果p1的狀態(tài)是pending,那么p2的回調(diào)函數(shù)就會(huì)等待p1的狀態(tài)改變;如果p1的狀態(tài)已經(jīng)是resolved或者rejected,那么p2的回調(diào)函數(shù)將會(huì)立刻執(zhí)行。
注意,調(diào)用resolve或reject并不會(huì)終結(jié) Promise 的參數(shù)函數(shù)的執(zhí)行。

new Promise((resolve, reject) => {
  resolve(1);
  console.log(2);
}).then(r => {
  console.log(r);
});
// 2
// 1

調(diào)用resolve(1)以后,后面的console.log(2)還是會(huì)執(zhí)行,并且會(huì)首先打印出來(lái)。這是因?yàn)榱⒓?resolved 的 Promise 是在本輪事件循環(huán)的末尾執(zhí)行,總是晚于本輪循環(huán)的同步任務(wù)。

一般來(lái)說(shuō),調(diào)用resolve或reject以后,Promise 的使命就完成了,后繼操作應(yīng)該放到then方法里面,而不應(yīng)該直接寫(xiě)在resolve或reject的后面。所以,最好在它們前面加上return語(yǔ)句,這樣就不會(huì)有意外。

new Promise((resolve, reject) => {
  return resolve(1);
  // 后面的語(yǔ)句不會(huì)執(zhí)行
  console.log(2);
})
  • Promise.prototype.then()
    Promise實(shí)例具有then方法,也就是說(shuō),then方法定義在原型對(duì)象Promise.prototype上的。他的作用是為Promise實(shí)例添加狀態(tài)改變時(shí)的回調(diào)函數(shù)。前面說(shuō)過(guò),then方法的第一個(gè)參數(shù)是resolved狀態(tài)的回調(diào)函數(shù) ,第二個(gè)參數(shù)是rejected狀態(tài)的回調(diào)函數(shù)。
    thenf方法返回的是一個(gè)新的Promise實(shí)例(不是原來(lái)那個(gè)Promise實(shí)例),因此可以采用鏈?zhǔn)綄?xiě)法,即then方法后面再調(diào)用另一個(gè)then方法
getJSON("posts.json").then(function (json){
           return post.json;
}).then(function(post){
           //...
});

依次指定了兩個(gè)回調(diào)函數(shù),第一個(gè)回到函數(shù)完成以后會(huì)將返回結(jié)果作為參數(shù),傳入第二個(gè)回調(diào)函數(shù)。
采用鏈?zhǔn)降膖hen,可以指定一組按照次序調(diào)用的回調(diào)函數(shù)。這時(shí)前一個(gè)回調(diào)函數(shù),有可能返回的還是一個(gè)promise對(duì)象(即有異步操作),這時(shí)后一個(gè)回調(diào)函數(shù),就會(huì)等待該promise對(duì)象的狀態(tài)發(fā)生變化,才會(huì)被調(diào)用。

getJSON("posts/1.json").then(function(post){
       return getJSON(post.commentURL);
}).then(function  funA(comments) {
         console.log("resolved:" ,comments);
}, function funB(err){
        console.log("rejected:", err);
});

上面第一個(gè)then方法指定的回調(diào)函數(shù),返回的是另一個(gè)promise對(duì)象,這時(shí),第二個(gè)then方法指定的回調(diào)函數(shù),就會(huì)等待這個(gè)新的Promise對(duì)象狀態(tài)發(fā)生變化。如果變?yōu)閞esolved,就調(diào)用funA,如果狀態(tài)變?yōu)閞ejected,就調(diào)用funB。改為箭頭函數(shù),代碼更簡(jiǎn)潔:

getJSON("/post/1.json").then(
post => getJSON(psot.commentURL)
).then(
 comments => console.log("resolved:",comments),
err => console.log("rejected:" ,err)
);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 00、前言Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)...
    夜幕小草閱讀 2,225評(píng)論 0 12
  • 摘自:阮一峰 http://es6.ruanyifeng.com/#docs/promise 一、首先,我們要弄明...
    泡杯感冒靈閱讀 852評(píng)論 0 4
  • Promiese 簡(jiǎn)單說(shuō)就是一個(gè)容器,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果,語(yǔ)法上說(shuō),Pr...
    雨飛飛雨閱讀 3,486評(píng)論 0 19
  • 目錄:Promise 的含義基本用法Promise.prototype.then()Promise.prototy...
    BluesCurry閱讀 1,563評(píng)論 0 8
  • Promise 對(duì)象 Promise 的含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,831評(píng)論 1 56

友情鏈接更多精彩內(nèi)容