亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入學(xué)習(xí)JavaScript中的promise

 更新時間:2022年06月24日 09:07:45   作者:??陳梵阿????  
這篇文章主要介紹了深入學(xué)習(xí)JavaScript中的promise,Promise對象的主要?途是通過鏈式調(diào)?的結(jié)構(gòu),將原本回調(diào)嵌套的異步處理流程,轉(zhuǎn)化成“對象.then().then()...”的鏈式結(jié)構(gòu)

為什么要用Promise?

我們知道JavaScript是單線程的,一次只能執(zhí)行一個任務(wù),會阻塞其他任務(wù)。因此,所有的網(wǎng)絡(luò)任務(wù)、游覽器事件等都是異步的,我們可以使用異步回調(diào)函數(shù)來進行異步操作。

有這么一個場景,我可以通過6個人能夠認識到任何一個人。但是我們不知道當前的人聯(lián)系到下一個人的時間是多久,假如這是一個異步的操作。

可以用如下代碼表示:

  function ConnectPeople(i) {
    console.log(`我聯(lián)系到了第${i}個人`);
    return i + 1;
  }
  let i = 1;
  setTimeout(() => {
    const result1 = ConnectPeople(i);
    setTimeout(() => {
      const result2 = ConnectPeople(result1);
      setTimeout(() => {
        const result3 = ConnectPeople(result2);
        setTimeout(() => {
          const result4 = ConnectPeople(result3);
          setTimeout(() => {
            const result5 = ConnectPeople(result4);
            setTimeout(() => {
              const result6 = ConnectPeople(result5);
              setTimeout(() => {
                const result7 = ConnectPeople(result6);
                setTimeout(() => {
                  const result8 = ConnectPeople(result7);
                  setTimeout(() => {
                    const result9 = ConnectPeople(result8);
                    setTimeout(() => {
                      const result10 = ConnectPeople(result9);
                    }, 10000);
                  }, 5000);
                }, 3000);
              }, 2000);
            }, 3000);
          }, 2000);
        }, 1000);
      }, 500);
    }, 2000);
  }, 1000);

image.png

如上所示,當我們聯(lián)系到了第一個人后,再去聯(lián)系第二個人,然后再去聯(lián)系第三個人...直到我聯(lián)系到了10個人。乍一看,代碼好像還挺規(guī)整,但是如果100個人,1000個人呢?由于回調(diào)很多,函數(shù)作為參數(shù)層層嵌套,就陷入了回調(diào)地獄。這種情況下,就像是金字塔一樣的代碼非常不利于閱讀。

但是還好,我們有解決辦法。

使用Promise解決異步控制問題

什么是Promise?

Promise對象的主要?途是通過鏈式調(diào)?的結(jié)構(gòu),將原本回調(diào)嵌套的異步處理流程,轉(zhuǎn)化成“對象.then().then()...”的鏈式結(jié)構(gòu),這樣雖然仍離不開回調(diào)函數(shù),但是將原本的回調(diào)嵌套結(jié)構(gòu),轉(zhuǎn)化成了連續(xù)調(diào)?的結(jié)構(gòu),這樣就可以在閱讀上編程上下左右結(jié)構(gòu)的異步執(zhí)?流程了。

因此,Promise的作?是解決“回調(diào)地獄”,他的解決?式是將回調(diào)嵌套拆成鏈式調(diào)?,這樣便可以按照上下順序來進?異步代碼的流程控制。 如下代碼所示,我們使用了Promise,代碼也從原先的金字塔形式轉(zhuǎn)變成了從上往下的執(zhí)行流程。

  function ConnectPeople(i) {
    console.log(`我聯(lián)系到了第${i}個人`);
    return i + 1;
  }
  const p = new Promise((resolve) => {
    setTimeout(() => {
      resolve(ConnectPeople(1));
    }, 1000);
  });
  p.then((v1) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(ConnectPeople(v1));
      }, 1000);
    });
  }).then((v2) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(ConnectPeople(v2));
      }, 1000);
    });
  });

image.png

Promise的結(jié)構(gòu)

根據(jù)上面的代碼案例,我們發(fā)現(xiàn)Promise需要通過new關(guān)鍵字同時傳入一個參數(shù)來創(chuàng)建,所以我們可以嘗試打印一下window對象console.log(window)(window 對象在瀏覽器中有兩重身份,一個是ECMAScript 中的 Global 對象,另一個就是瀏覽器窗口的 JavaScript 接口),可以發(fā)現(xiàn)存在一個Promise的構(gòu)造函數(shù)。

image.png

Promise初始化的時候需要傳入一個函數(shù),如下所示:

const p = new Promise(fn) // fn是初始化的時候調(diào)用的函數(shù),它是同步的回調(diào)函數(shù)

回調(diào)函數(shù)

什么是回調(diào)函數(shù)?

JavaScript中的回調(diào)函數(shù)結(jié)構(gòu),默認是同步的結(jié)構(gòu)。由于JavaScript單線程異步模型的規(guī)則,如果想要編寫異步的代碼,必須使?回調(diào)嵌套的形式才能實現(xiàn),所以回調(diào)函數(shù)結(jié)構(gòu)不?定是異步代碼,但是異步代碼?定是回調(diào)函數(shù)結(jié)構(gòu)。

為什么異步代碼一定是回調(diào)函數(shù)結(jié)構(gòu)?

我們知道JavaScript是單線程異步模型,嚴格按照同步在前異步在后的順序執(zhí)行。如果用默認的上下結(jié)構(gòu),我們拿不到異步回調(diào)中的結(jié)果。

如下所示,代碼執(zhí)行的時候會先執(zhí)行同步代碼,異步代碼會掛起,繼續(xù)執(zhí)行同步代碼,到1s的時候掛起的任務(wù)會進入隊列,到2s的時候會繼續(xù)執(zhí)行同步代碼打印1,然后從任務(wù)隊列中取任務(wù)將num變成100,打印num。 所以實際執(zhí)行效果是,過2秒后,先打印1再打印100。

var num = 1;
setTimeout(()=>{
    num = 100
    console.log(num)
},0)
var d = new Date().getTime()
var d1 = new Date().getTime()
while ( d1 - d < 1000 ) {
    d1 = new Date().getTime()
}
console.log(num) // 1

刨析Promise

翻譯一下promise,結(jié)果是承諾,保證,紅寶書中的解釋是期約。

它有三個狀態(tài):

  • pending 待定,初始狀態(tài)
  • fulfilled 兌現(xiàn),已完成,通常代表成功執(zhí)行了某一任務(wù)。初始化函數(shù)中的resolve()執(zhí)行時,狀態(tài)就會變味fulfilled,而且.then函數(shù)注冊的回調(diào)會開始執(zhí)行,resolve中傳遞的參數(shù)會進入回調(diào)函數(shù)成為形參。
  • rejected 拒絕,通常代表執(zhí)行一次任務(wù)失敗,調(diào)用reject()時,catch注冊的函數(shù)就會觸發(fā),并且reject中傳遞的內(nèi)容會變成回調(diào)函數(shù)的形參。

三種狀態(tài)之間的關(guān)系:

當對象創(chuàng)建之后同?個Promise對象只能從pending狀態(tài)變更為fulfilled或rejected中的其中?種,并且狀態(tài)?旦變更就不會再改變,此時Promise對象的流程執(zhí)?完成并且finally函數(shù)執(zhí)?。

image.png

我們打印一下Promise對象,發(fā)現(xiàn)它的構(gòu)造函數(shù)中定義了all、allSettled、any、racereject、resolve方法(這些是實例方法),它的原型上存在catch、finallythen方法(這些是原型方法)。

原型方法——catch\finally\then

首先看下面代碼:

  new Promise(function (resolve, reject) {
    resolve();
    reject();
  })
    .then(function () {
      console.log("then執(zhí)?");
    })
    .catch(function () {
      console.log("catch執(zhí)?");
    })
    .finally(function () {
      console.log("finally執(zhí)?");
    });

執(zhí)行后依次打印then執(zhí)行->finally執(zhí)行,發(fā)現(xiàn).catch的回調(diào)沒有執(zhí)行。

再看如下代碼:

  new Promise(function (resolve, reject) {
    reject();
    resolve();
  })
    .then(function () {
      console.log("then執(zhí)?");
    })
    .catch(function () {
      console.log("catch執(zhí)?");
    })
    .finally(function () {
      console.log("finally執(zhí)?");
    });

這個串代碼和之前的代碼唯一的不同在于Promise中的回調(diào)先執(zhí)行了resolve()還是先執(zhí)行了reject(),打印結(jié)果是catch執(zhí)行->finally執(zhí)行,發(fā)現(xiàn).then的回調(diào)沒有執(zhí)行。

那如果Promise的回調(diào)不執(zhí)行reject()和resolve()呢?

會發(fā)現(xiàn)什么輸出都沒有!

注意:Promise.prototype.catch()其實是一個語法糖,相當于是調(diào)用 Promise.prototype.then(null, onRejected)。.then中其實是可以傳入2個回調(diào)函數(shù),第一個回調(diào)函數(shù)是resolve()后執(zhí)行,第二個回調(diào)函數(shù)是reject()后執(zhí)行,2個是互斥的。

這是因為Promise的異步回調(diào)部分如何執(zhí)?,取決于我們在初始化函數(shù)中的操作,并且初始化函數(shù)中?旦調(diào)?了resolve后?再執(zhí)?reject也不會影響then執(zhí)?,catch也不會執(zhí)?,反之同理。

?在初始化回調(diào)函數(shù)中,如果不執(zhí)?任何操作,那么promise的狀態(tài)就仍然是pending,所有注冊的回調(diào)函數(shù)都不會執(zhí)?。

由此可見,執(zhí)行完resolve()之后才能夠執(zhí)行.then的回調(diào);執(zhí)行reject()之后才能夠執(zhí)行.catch的回調(diào);finally()的回調(diào)會在執(zhí)行完.then或.catch之后執(zhí)行。

這時候,我們就會想,是不是可以把resolve或者reject的調(diào)用設(shè)定在異步函數(shù)內(nèi)去調(diào)用,這樣是不是就能解決回調(diào)地獄的問題了?

所以我們就去嘗試一下:

  new Promise(function (resolve, reject) {
    setTimeout(() => {
      console.log(111);
      resolve();
    }, 2000);
  })
    .then(function () {
      return new Promise(function (resolve, reject) {
        setTimeout(() => {
          console.log(222);
          resolve();
        }, 2000);
      });
    })
    .then(function () {
      return new Promise(function (resolve, reject) {
        setTimeout(() => {
          console.log(333);
          resolve();
        }, 2000);
      });
    })
    .catch(function () {
      console.log("catch執(zhí)?");
    })
    .finally(function () {
      console.log("finally執(zhí)?");
    });

上面代碼每隔2s依次打印111->222->333 finally執(zhí)行。333執(zhí)行后立馬執(zhí)行finally。

為什么要在.then的回調(diào)函數(shù)中return一個Promise呢?

因為下一個異步的執(zhí)行,需要等待前一個異步執(zhí)行完畢后才調(diào)用,我們需要用到resolve來控制.then執(zhí)行的時機。

那如果我們不指明return返回值,它會返回什么呢?是如何實現(xiàn)鏈式調(diào)用呢?

看下面代碼:

  const p2 = new Promise((resolve, reject) => {
    resolve();
  });
  const p3 = p2.then(() => {
    console.log("resolved");
  });
  console.log(p3, 111);

p2.then的回調(diào)函數(shù)中沒有return,但是我們知道一般來說函數(shù)返回值默認返回undefined,但是undefined中不會存在.then的方法。

因此我們就看一下p3里面到底是什么。有些人會想,.then是異步調(diào)用的,它是一個微任務(wù),那訪問p3是不是不太正確?

我們打印一下p3,就會看到如下信息:

image.png

第一行p3的狀態(tài)還是pending,當我們點開,發(fā)現(xiàn)已經(jīng)變成了fulfilled了,因為引用類型是按地址訪問的,當我們點開的時候會發(fā)現(xiàn)指向這個地址里最后的數(shù)據(jù)是什么。普通對象同理。

如下所示,我們console的時候a對象的name還是a,但是我們點開后發(fā)現(xiàn)程序執(zhí)行完后a對象的實際name變成了b。

  const a = { name: "a" };
  console.log(a);
  a.name = "b";

image.png

回歸正傳,我們發(fā)現(xiàn)p3里面有3個字段,[[Prototype]]我們很熟悉,這個是一個指向當前對象原型的指針。在大多數(shù)游覽器中是可以通過__proto__訪問到的。

我們嘗試著去訪問:

  console.log(p3, 111);
  console.log(p3.__proto__);
  console.log(p3.__proto__ === Promise.prototype); // true

image.png

我們可以看到.then默認返回的有3個字段,然后通過原型鏈來實現(xiàn)鏈式調(diào)用:

  • [[Prototype]]代表Promise的原型對象
  • [[PromiseState]]代表Promise對象當前的狀態(tài)
  • [[PromiseResult]]代表Promise對象的值,分別對應(yīng)resolve或reject傳?的結(jié)果

本質(zhì)就是在我們調(diào)?這些?持鏈式調(diào)?的函數(shù)的結(jié)尾時,他?返回了?個包含他??的對象或者是?個新的??,這些?式都可以實現(xiàn)鏈式調(diào)?。

中斷鏈式調(diào)用的方式:

中斷的?式可以使?拋出?個異?;蚍祷?個rejected狀態(tài)的Promise對象

鏈式調(diào)用的基本形式:

  • 只要有then()并且觸發(fā)了resolve,整個鏈條就會執(zhí)?到結(jié)尾,這個過程中的第?個回調(diào)函數(shù)的參數(shù)是resolve傳?的值
  • 后續(xù)每個函數(shù)都可以使?return返回?個結(jié)果,如果沒有返回結(jié)果的話下?個then中回調(diào)函數(shù)的參數(shù)就是undefined
  • 返回結(jié)果如果是普通變量,那么這個值就是下?個then中回調(diào)函數(shù)的參數(shù)
  • 如果返回的是?個Promise對象,那么這個Promise對象resolve的結(jié)果會變成下?次then中回調(diào)的函數(shù)的參數(shù)
  • 如果then中傳?的不是函數(shù)或者未傳值,Promise鏈條并不會中斷then的鏈式調(diào)?,并且在這之前最后?次的返回結(jié)果,會直接進?離它最近的正確的then中的回調(diào)函數(shù)作為參數(shù)

前面幾條我們都能懂,第5條什么意思的? 看下面代碼:

  const p2 = new Promise((resolve, reject) => {
    console.log(1);
    resolve();
  });
  p2.then(() => {
    console.log(2);
    return 123;
  })
    .then()
    .then("456")
    .then((res) => {
      console.log(res);
    });

image.png

發(fā)現(xiàn)只打印了1 2 和 123,return的123進入了最后一個.then的回調(diào)函數(shù)中作為參數(shù)。

resolve和reject

至于resolve和reject,我們通過上面已經(jīng)知道了resolve和reject能夠更改Promise的狀態(tài),而Promise的狀態(tài)是不可逆的,且是私有的。所以我們必須在Promise內(nèi)部調(diào)用resolve或者reject。

當然,resolve和reject也能夠傳入?yún)?shù),而傳入的參數(shù),會變?yōu)?then或.catch的回調(diào)函數(shù)中的參數(shù)。

那如果傳入一個Promise作為參數(shù)呢???

resolve()

實際上,如果在resolve中傳入一個promise,那它的行為就相當于是一個空包裝。Promise.resolve()可以說相當于是一個冪等方法,會保留傳入期約的狀態(tài)。

let p = Promise.resolve(7);
setTimeout(console.log, 0, p === Promise.resolve(p)); // true
setTimeout(console.log, 0, p === Promise.resolve(Promise.resolve(p))); // true

reject()

會實例化一個拒絕的期約并拋出一個異步錯誤,不能通過try...catch捕獲,只能通過拒絕處理程序捕獲。

如果給reject傳入一個promise,則這個promise會成為返回的拒絕promise的理由。

  const p1 = new Promise(() => {});
  const p2 = Promise.resolve(111);
  const r3 = Promise.reject(p1);
  const r4 = Promise.reject(p2);
  console.log(r3);
  console.log(r4);

image.png

Promise常用API——all()、allSettled()、any()、race()

all()

假如我們有一個需求,一個頁面需要請求3個接口才能渲染,并且要求3個接口必須全部返回。如果我們通過鏈式調(diào)用的方式,接口1請求了再去請求接口2然后去請求接口3,全都成功了再去渲染頁面。這種就很耗時,所以就有了一個all的方法來解決。

Promise.all([promise對象,promise對象,...]).then(回調(diào)函數(shù))

Promise.all()的參數(shù)是一個Promise數(shù)組,只有數(shù)組中所有的Promise的狀態(tài)變成了fulfilled之后才會執(zhí)行.then回調(diào)的第一個回調(diào)函數(shù),并且將每個Promise結(jié)果的數(shù)組變?yōu)榛卣{(diào)函數(shù)的參數(shù)。如果Promise中有一個rejected,那么就會觸發(fā).catch()的回調(diào)。

  let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("第?個promise執(zhí)?完畢");
    }, 1000);
  });
  let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("第?個promise執(zhí)?完畢");
    }, 2000);
  });
  let p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("第三個promise執(zhí)?完畢");
    }, 3000);
  });
  Promise.all([p1, p3, p2])
    .then((res) => {
      console.log(res);
    })
    .catch(function (err) {
      console.log(err);
    });
    // 3s后打印?['第?個promise執(zhí)?完畢', '第三個promise執(zhí)?完畢', '第?個promise執(zhí)?完畢']

race()

race()方法與all()方法的使用格式相同,不同的是,回調(diào)函數(shù)的參數(shù)是promise數(shù)組中最快執(zhí)行完畢的promise的返回值,它的狀態(tài)可能是fulfilled也有可能是rejected,但是是最快返回的。

根據(jù)race這個單詞就能理解,相當于一群promise進行比賽,誰先到終點第一就是誰,不管是男是女。

  //promise.race()相當于將傳?的所有任務(wù)進行一個競爭
  let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("第?個promise執(zhí)?完畢");
    }, 5000);
  });

  let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("第?個promise執(zhí)?完畢");
    }, 2000);
  });
  let p3 = new Promise((resolve) => {
    setTimeout(() => {
      resolve("第三個promise執(zhí)?完畢");
    }, 3000);
  });
  Promise.race([p1, p3, p2])
    .then((res) => {
      console.log(res);
    })
    .catch(function (err) {
      console.error(err);
    });
    // 2秒后打印第二個promise執(zhí)行完畢

allSettled()

該方法需要傳入所有不在pendding狀態(tài)的promise數(shù)組,然后通過該方法可以知道數(shù)組中的promise的當前狀態(tài)。

當有多個彼此不依賴的異步任務(wù)成功完成時,或者總是想知道每個promise的結(jié)果時,通常使用它。

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));

// "fulfilled"
// "rejected"

any()

這個方法目前還是實驗性的,不是所有的游覽器都能夠支持。

接受一個promise數(shù)組,只要有一個promise的狀態(tài)變成了fulfilled,那么這個方法就會返回這個promise;

如果所有的promise的狀態(tài)都是rejected,那么就返回失敗的promise,并且把單一的錯誤集合在一起。

const pErr = new Promise((resolve, reject) => {
  reject("總是失敗");
});
const pSlow = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, "最終完成");
});
const pFast = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "很快完成");
});
Promise.any([pErr, pSlow, pFast]).then((value) => {
  console.log(value);
})
// 很快完成

到此這篇關(guān)于深入學(xué)習(xí)JavaScript中的promise的文章就介紹到這了,更多相關(guān)JavaScript promise內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論