js promise理解

       由於昨天發了一篇關於setTimeout 的文章,裏面提到了 Promise ,那篇文章裏沒有解釋Promise的用法和含義,因爲昨天的我還沒太懂Promise,所以沒有在那篇文章繼續解釋Promise,然後今天的我總算是對Promise有所理解了,然後我來談談我學到的Promise的知識,因爲是個人的理解,所以會不全面,請多包涵。

一、何爲Promise

在MDN web docs 裏面是這麼解釋 Promise的:

Promise 對象是一個代理對象(代理一個值),被代理的值在Promise對象創建時可能是未知的。它允許你爲異步操作的成功和失敗分別綁定相應的處理方法(handlers)。 這讓異步方法可以像同步方法那樣返回值,但並不是立即返回最終執行結果,而是一個能代表未來出現的結果的promise對象

 看完這段話我的內心一陣無語,我就只能怪我自己的理解能力好像沒有達到水準一樣,並不完全懂這段話在說什麼,這讓我一度懷疑我這智商是不是不夠用了,怎麼就沒理解這段話說的是什麼意思。很好,由於我們的大腦是一個無窮大的海綿,它可以無限的吸收知識,然後通過我們的意念控制我們的大腦去理解並消化了這些知識,然後得到下面的結果。

只要我們的英語水平是初中生的水平,就可以知道 promise 這個英語單詞的意思是 “承諾”。是的Promise就是“承諾” 的意思。

簡單的說 Promise 就是: 小花馬上就要過她生日了,然後她的好閨蜜小麗承諾說在她生日的時候會送給她一件漂亮的衣服給她。

好了小花獲得了小麗給她的承諾。但是,天知道這承諾會不會實現的,未知的因素很多,不能絕對的認爲這 小麗給小花的Promise就一定能夠實現。

所以Promise 有了三種可能的狀態:

1.pending(待定的):小花不知道小麗能補能給她漂亮衣服,她只能等待她生日的時候的到來

2.fulfilled(已解決/以實現):到了生日那天小麗真的給小花一件漂亮的衣服,小麗實現了她的承諾

3.rejected(已拒絕/沒有實現):小麗忘了小花的生日,所以沒有送漂亮衣服給小花

然後我們來了解一下Promise的特點:

1.promise是一個異步操作, 上面不是給了promise的三種狀態嗎,只有異步操作的結果纔可以決定當前promise的狀態,因爲promise 的意思爲“承諾”,是比較嚴肅正經的,所以任何操作都不能改變當前promise的狀態。

2.第1點已經提到任何操作均不能改變當前promise的狀態,所以promise的狀態不能從‘未來’回到‘現在’,‘未來’也不能回到‘現在’,即不能從 fulfilled rejected 回到 pending fulfilled rejected之間不能相互轉換。只有兩種情況的轉換:

1)從pending轉換成fulfilled

2)從pending轉換成rejected

可以這樣理解:小麗給小花的承諾在小花生日之前是小花是不知道小麗能不能送他衣服,這時候是現在時的 pending ,然後未來到了小花生日那天,只有兩種可能:要麼小麗確實送給小花衣服了,也就是實現了承諾,小麗給小花的Promise 的狀態就從 pending變成了fulfilled ;要麼小麗把小花的生日給忘了,沒有給小花送衣服,沒有實現到承諾,小麗給小花的Promise狀態就從pending變成了rejected。這狀態不能從fulfilledrejected變成pending的,畢竟時間不能倒回去啊,而fulfilledrejected之間也不能轉換,因爲已經發生的狀態是既定的了,承諾實現了就是實現了,沒實現就是沒實現,不能從實現又變成沒實現的。

我們來看看阮一峯大大是怎麼總結的:

(1)對象的狀態不受外界影響,promise對象代表一個異步操作,有三種狀態,pending(進行中)、fulfilled(已成功)、rejected(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態,這也是promise這個名字的由來“承若”;
(2)一旦狀態改變就不會再變,任何時候都可以得到這個結果,promise對象的狀態改變,只有兩種可能:從pending變爲fulfilled,從pending變爲rejected。這時就稱爲resolved(已定型)。如果改變已經發生了,你再對promise對象添加回調函數,也會立即得到這個結果,這與事件(event)完全不同,事件的特點是:如果你錯過了它,再去監聽是得不到結果的。

 二、Promise的語法

語法的話當然是MDN web docs 裏寫的才比較官方:

new Promise( function(resolve, reject) {...} /* executor */  );

我們繼續用上面的例子來解釋這Promise對象裏面的 resolve reject 是什麼。

首先我們得把上面那個例子用代碼來實現:

var isLiForget = false; //給一個布爾值判斷小麗有沒有忘記小花的生日
var getCloth = new Promise(function(resolve,reject){
    if(!isLiForget){ //沒忘記
        var cloth = {
            color:'red',
            price:'$120'
        };
        resolve(cloth); // 得到衣服
    }else{
        var err = new Error("forgot the promise"); //忘記了
        reject(err);
    }
});
 
//之後就是調用Promise了
var testFn = function(){
    getCloth.then(function(fulfilled){
        console.log(fulfilled);
    }).catch(function(rejected){
        console.log(rejected.message);
    });
}
testFn();

1.給一個布爾值 isLiForget 來判斷小麗是否忘記承諾

2.創建一個承諾Promise對象名爲 getCloth var getCloth = new Promise(function(resolve,reject){ // }); 這裏的resolve ,和reject參數是函數,當承諾實現了的時候,就會調用resolve函數,然後對應的promise的狀態就變成fulfilled;當承諾沒有實現的時候,就會調用reject函數,其狀態變成了rejected。 是否還記得創建promise的語法:

new Promise( function(resolve, reject) {...} /* executor */  );

這裏的executor 是一個函數。就是new Promise 括號括着的函數叫做executorresolvereject函數是作爲executor 的參數被傳進去的,當然這兩個參數是可選填的。resolvereject函數如果被調用了就會把函數裏的結果作爲參數傳遞出去;resolve是把函數裏的結果傳遞出去,而reject就說明是承諾沒實現,這相當於是出錯誤了,所以它會把報錯的信息作爲參數傳遞出去。

3.當我們把 promise 一切部署好後,我們就得用到這個 promise 了。我們定義一個 testFn 函數來調用 promise 。當小花她知道小麗給她送衣服的承諾後,她心中就會安排這: “我得到衣服後我要買一雙新鞋子搭配這衣服”,或者如果小麗沒有送衣服給小花,小花就會生氣,所以如果有了promise,我們就會用到 .then() .catch() 函數來實現我們有了這個promise後所採取的措施。

.then()有兩個參數:onFulfilled onRejected ,這兩個參數看英文也知道是什麼意思,這兩個參數也是函數,onFulfilled當然是Promise 實現的時候調用,onRejected就是Promise被拒絕的時候調用的。我們來看看MDN怎麼說:

當Promise變成接受狀態(fulfillment)時,該參數作爲回調函數被調用(參考: Function)。該函數有一個參數,即接受的最終結果(the fulfillment  value)。如果傳入的 onFulfilled 參數類型不是函數,則會在內部被替換爲(x) => x ,即原樣返回 promise 最終結果的函數

onRejected

當Promise變成拒絕狀態(rejection )時,該參數作爲回調函數被調用(參考: Function)。該函數有一個參數,,即拒絕的原因(the rejection reason)

.then() 有兩種寫法任君選擇

p.then(onFulfilled, onRejected);
 
p.then(function(value) {
   // fulfillment
  }, function(reason) {
  // rejection
});

.catch() 是當promise沒有實現的時候,狀態爲rejected時被使用。這就好像ajax裏面當ajax請求成功就會調用success函數,請求失敗則調用error函數。同樣的.catch()也有兩種寫法,只是.catch()只有一個參數而已。

.then() 裏面的函數有一個fulfilled的參數,這個參數的值就是promise實現後調用resolve()所返回的值,在這裏就是 cloth ;同理.catch() 裏面的一個rejected參數的值就是promise沒有實現而調用reject()所返回的錯誤信息值在這個例子就是 err

當 isLiForget = false; 時:

isLiForget = true; 時:

 三、Promise最主要的特色——鏈式調用

爲什麼使用Promise可以鏈式調用呢? 事實上,Promise.then()方法其返回值是一個新的promise對象,相同類型的對象是可以鏈式調用的。

我們繼續那小花的例子來說。如果小花得到了衣服她就有了第二個想法,即可以理解爲另外一個承諾Promise:“我要買雙新鞋子搭配我的衣服”。我們來實現一下:

//聲明一個買鞋的函數,裏面返回promise
var buyShoes = function(cloth){
    var shoes = {
        color:"white",
        brand:"nike"
    }
    var msg = {
        message:"I bougth a pair of "+shoes.color+" "+shoes.brand+"shoes to match my "+cloth.color+" cloth.",
	shoes:shoes,
	cloth:cloth
	}
    return Promise.resolve(msg);
}
var testFn = function(){
    getCloth
      .then(buyShoes)
      .then(function(fulfilled){
        console.log(fulfilled);
    }).catch(function(rejected){
        console.log(rejected.message);
    });
}
testFn();

顯然這裏的.then()方法值寫了onFulfilled的參數,省略了onRejected參數。getCloth.then().then() 就是一個鏈式調用。運行的結果:

 這是 isLiForget = false 時的結果, 這個值爲true 那輸出仍然是之前那個。

 四、Promise與異步

Promise是異步的。js異步操作是通過js的事件循環機制EventLoop實現的。這裏引用以下文章所寫的內容,這篇文章很詳細的寫了異步是什麼  https://blog.csdn.net/li123128/article/details/80650256

當JS解析執行時,會被引擎分爲兩類任務,同步任務(synchronous) 和 異步任務(asynchronous)

對於同步任務來說,會被推到執行棧按順序去執行這些任務。
對於異步任務來說,當其可以被執行時,會被放到一個 任務隊列(task queue) 裏等待JS引擎去執行。

當執行棧中的所有同步任務完成後,JS引擎纔會去任務隊列裏查看是否有任務存在,並將任務放到執行棧中去執行,執行完了又會去任務隊列裏查看是否有已經可以執行的任務。這種循環檢查的機制,就叫做事件循環(Event Loop)

對於任務隊列,其實是有更細的分類。其被分爲 微任務(microtask)隊列 & 宏任務(macrotask)隊列

宏任務: setTimeout、setInterval等,會被放在宏任務(macrotask)隊列。

微任務: Promise的then、Mutation Observer等,會被放在微任務(microtask)隊列。

Event Loop的執行順序是:

  1. 首先執行執行棧裏的任務。
  2. 執行棧清空後,檢查微任務(microtask)隊列,將可執行的微任務全部執行。
  3. 取宏任務(macrotask)隊列中的第一項執行。
  4. 回到第二步。 

注意: 微任務隊列每次全執行,宏任務隊列每次只取一項執行。

var testFn = function(){ console.log("promise before"); //同步任務 //異步宏任務 setTimeout(function(){ console.log("異步宏任務"); },300); //異步微任務 getCloth.then(function(fulfilled){ console.log(fulfilled); }).catch(function(rejected){ console.log(rejected.message); }); console.log("promise after");//同步任務 } testFn();

總結起來js引擎對程序的執行順序是:1。先執行同步任務的程序 2。 在執行異步任務裏的微任務 3。所有微任務都執行完了後就執行異步的宏任務,但這裏是一個一個宏任務去執行,不是一下子執行完。

var testFn = function(){
    console.log("promise before"); //同步任務
    //異步宏任務
    setTimeout(function(){
        console.log("異步宏任務");
    },300);
    //異步微任務
    getCloth.then(function(fulfilled){
        console.log(fulfilled);
    }).catch(function(rejected){
        console.log(rejected.message);
    });
    console.log("promise after");//同步任務
}
testFn();

上圖結果證明了上述所說的js的執行順序 。

這些總結就是我理解到的Promise

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章