欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解JavaScript如何實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Promise對(duì)象

 更新時(shí)間:2022年11月30日 16:09:47   作者:寒月十九  
Promise對(duì)象的作用將異步操作以同步操作的流程表達(dá)出來,避免層層嵌套的回調(diào)函數(shù),而且Promise提供了統(tǒng)一的接口,使得控制異步操作更加容易。本文介紹了如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Promise對(duì)象,需要的可以參考一下

前言

實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Promise對(duì)象,我們首先要了解幾個(gè)相關(guān)的知識(shí)點(diǎn):

Promise對(duì)象的狀態(tài): pending(進(jìn)行中)、fulfilled(已成功)和rejected(已失?。?。只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無(wú)法改變這個(gè)狀態(tài)。這也是Promise這個(gè)名字的由來,它的英語(yǔ)意思就是“承諾”,表示其他手段無(wú)法改變。

Promise的參數(shù): Promise構(gòu)造函數(shù)接收一個(gè)函數(shù)作為參數(shù),函數(shù)內(nèi)部有兩個(gè)參數(shù),分別是resolve和reject,這兩個(gè)參數(shù)是兩個(gè)函數(shù),由JS引擎提供,不需要我們部署。reslove函數(shù)的作用是將Promise對(duì)象的狀態(tài)由 'pending' 狀態(tài)變?yōu)?'resolved'狀態(tài)即('fulfilled'狀態(tài)),方便與參數(shù)名對(duì)應(yīng),reject函數(shù)的作用是將Promise對(duì)象的狀態(tài)由 'pending' 狀態(tài)變?yōu)?'rejected'狀態(tài)。

但是我們應(yīng)該注意的是,Promise對(duì)象的狀態(tài)一經(jīng)改變,就不再發(fā)生改變(即pending --> resolved || pending --> rejected 其中任意一種發(fā)生改變之后,Promise對(duì)象的狀態(tài)將不再發(fā)生改變)

Promise的基礎(chǔ)結(jié)構(gòu)與用法

 let p1 = new Promise((resolve, reject) => {
   resolve('成功');
   reject('失敗');
   throw('報(bào)錯(cuò)');  //相當(dāng)于reject()
 })

 console.log(p1);

讓我們看看三種狀態(tài)的打印的結(jié)果分別是什么吧

使用class類實(shí)現(xiàn)promise對(duì)象

class myPromise {
  constructor(executor) {
    this.status = 'pending'; // 變更promise的狀態(tài)
    this.value = null;
    
    executor(this.resolve, this.reject); // new 一個(gè)myPromise 得到的實(shí)例對(duì)象里面有兩個(gè)函數(shù)
  }

  resolve(value) {
    if (this.status !== 'pending') return;
    this.status = 'fulfilled'; // 變更promise的狀態(tài)
    this.value = value;
  }

  reject(reason) {
    if (this.status !== 'pending') return
    this.status = 'rejected';
    this.value = reason;
  }
}

貌似這么寫代碼邏輯也說得通,那么讓我們看一下實(shí)現(xiàn)的效果:

看到這里,不難想到我們的resolve和reject的兩個(gè)方法的this指向出現(xiàn)了問題,我們仔細(xì)看看不難發(fā)現(xiàn),這兩個(gè)方法被我們作為實(shí)參放到了構(gòu)造器函數(shù),此時(shí)this的指向是指向了構(gòu)造器函數(shù),而不是我們寫的myPromise這個(gè)構(gòu)造函數(shù)身上,那只需要bind顯示綁定一下this 的指向就可以解決了。

executor(this.resolve.bind(this), this.reject.bind(this))

并且myPromise的狀態(tài)一經(jīng)變更也不再改變,是不是有一點(diǎn)原裝Promise的味道了。但是在Promise里面還有一個(gè)錯(cuò)誤捕捉機(jī)制,只要promise里面執(zhí)行的邏輯報(bào)錯(cuò)了,就需要走reject邏輯,將錯(cuò)誤拋出來,那我們只需要使用try catch來實(shí)現(xiàn)就可以。

try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error)
    }

這樣我們就實(shí)現(xiàn)了極簡(jiǎn)版的Promise對(duì)象,但是通常情況下我們都是使用Promise對(duì)象來處理異步的問題,說到異步,那不得不提起Promise.prototype.then()這個(gè)方法了,then方法返回的是一個(gè)新的Promise實(shí)例(注意,不是原來那個(gè)Promise實(shí)例)。因此可以采用鏈?zhǔn)綄懛?,即then方法后面再調(diào)用另一個(gè)then方法。

then方法的第一個(gè)參數(shù)是`resolved狀態(tài)的回調(diào)函數(shù)`,第二個(gè)參數(shù)是`rejected狀態(tài)的回調(diào)函數(shù)`,它們都是可選的。

當(dāng)promise的狀態(tài)為'fulfilled'會(huì)執(zhí)行第一個(gè)回調(diào)函數(shù),當(dāng)狀態(tài)為'rejected'時(shí)執(zhí)行第二個(gè)回調(diào)函數(shù)。

必須等到Promise的狀態(tài)變更過一次之后,狀態(tài)為'fulfilled'或者'rejected',才去執(zhí)行then里面的邏輯。

.then支持鏈?zhǔn)秸{(diào)用,下一次.then受上一次.then執(zhí)行結(jié)果的影響。

知道以上這幾點(diǎn),我們就可以嘗試如何實(shí)現(xiàn).then方法了

class myPromise {
  constructor(executor) {
    this.status = 'pending'; 
    this.value = null;
    this.onFulfilledCallbacks = []; // 用來保存成功的回調(diào)(處理異步)
    this.onRejectedCallbacks = []; // 用來保存失敗的回調(diào)(處理異步)
    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error)
    }
  }

  resolve(value) {
    if (this.status !== 'pending') return;
    this.status = 'fulfilled';
    this.value = value;
    // 調(diào)用then里面的回調(diào)
    while (this.onFulfilledCallbacks.length) { // 當(dāng)異步成功回調(diào)數(shù)組中存在回調(diào)函數(shù),那就執(zhí)行
      this.onFulfilledCallbacks.shift()(this.value)
    }
  }

  reject(reason) {
    if (this.status !== 'pending') return
    this.status = 'rejected';
    this.value = reason;
    while (this.onRejectedCallbacks.length) { // 當(dāng)異步失敗回調(diào)數(shù)組中存在回調(diào)函數(shù),那就執(zhí)行
      this.onRejectedCallbacks.shift()(this.value)
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val  // 判斷.then的第一個(gè)參數(shù)是不是一個(gè)函數(shù),如果不是就直接作為結(jié)果返回
    onRejected = typeof onRejected === 'function' ? onRejected : val => { throw val } // 判斷.then的第二個(gè)參數(shù)是不是一個(gè)函數(shù),如果不是就直接作為錯(cuò)誤返回

    var thenPromise = new myPromise((resolve, reject) => {  // 因?yàn)?then返回的是一個(gè)心的Promise對(duì)象

      const resolvePromise = callback => {  // 用于判斷回調(diào)函數(shù)的類型
        setTimeout(() => {   // 讓整個(gè)回調(diào)函數(shù)比同步代碼晚一點(diǎn)執(zhí)行,官方不是使用setTimeout實(shí)現(xiàn)
          try {
            const x = callback(this.value);
            if (x === thenPromise) {  // 你正在返回自身
              throw new Error('不允許返回自身!');
            }
            if (x instanceof myPromise) { // 返回的是一個(gè)Promise對(duì)象
              x.then(resolve, reject);
            } else { // 直接返回一個(gè)值,作為resolve的值,傳遞給下一個(gè).then
              resolve(x);
            }
          } catch (error) {
            reject(error);
            throw new Error(error)
          }
        })
      }

      if (this.status === 'fulfilled') {
        resolvePromise(onFulfilled)
      } else if (this.status === 'rejected') {
        resolvePromise(onRejected)
      } else if (this.status === 'pending') {
        this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled));
        this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected));
      }
    })
    return thenPromise
  }
}

寫在最后

最后和大家分享一下,我是如何一步一步實(shí)現(xiàn)簡(jiǎn)易版的Promise

首先從Promise構(gòu)造函數(shù)的特點(diǎn),三種狀態(tài),狀態(tài)一經(jīng)改變就不再變化,所以在resolvereject的方法里面加上判斷,如果不是'pending'狀態(tài),則直接return,這樣就實(shí)現(xiàn)了狀態(tài)一經(jīng)發(fā)生改變則不再變化,因?yàn)?then里面回調(diào)的執(zhí)行,是根據(jù)Promise的狀態(tài)來執(zhí)行,當(dāng)狀態(tài)為'fulfilled'時(shí)才執(zhí)行.then第一個(gè)回調(diào)函數(shù),裝狀態(tài)為'rejected'執(zhí)行.then第二個(gè)回調(diào)函數(shù),但是如果在Promise里面,在resolve或者reject的外面套上setTimeout,那么狀態(tài)變更會(huì)加入到下一次宏任務(wù)隊(duì)列里,那我們九就維護(hù)出兩個(gè)數(shù)組,用來存放未執(zhí)行的回調(diào),當(dāng)狀態(tài)改變之后,在對(duì)應(yīng)的resolvereject方法里去判斷我們維護(hù)的未執(zhí)行的回調(diào)函數(shù)的數(shù)組里是否有未執(zhí)行的回調(diào),如果有直接調(diào)用掉,并且因?yàn)?then返回的是一個(gè)Promise對(duì)象,所以我們不能直接把'onFulfilled',或者'onRejected'其中一個(gè)回調(diào)給返回出去,否則.then后面就不能再接.then,所以在then方法里面我們定義了一個(gè)resolvePromise函數(shù),其目的就是在返回的'onFulfilled',或者'onRejected'外面套一層Promise對(duì)象,使得他后面能繼續(xù)接.then的回調(diào),在這個(gè)resolvePromise函數(shù)內(nèi)部我們還添加了判斷回調(diào)的類型,在官方的定義的Promise對(duì)象中,規(guī)定了回調(diào)不能是原Promise對(duì)象,另外兩個(gè)判斷是回調(diào)是一個(gè)Promise對(duì)象,以及如果不是Promise對(duì)象,那就直接resolve()出去

最后同步代碼會(huì)優(yōu)先于.then的執(zhí)行,因?yàn)?code>.then是異步代碼中的微任務(wù),只有宏任務(wù)執(zhí)行完之后,微任務(wù)才會(huì)執(zhí)行,所以在resolvePromise的回調(diào)外面套一層setTimeout,這樣返回出去的.then的邏輯,會(huì)去到下一次的宏任務(wù)隊(duì)列,這樣就實(shí)現(xiàn)了.then的執(zhí)行會(huì)比同步代碼稍晚一些,但是官方并不是使用setTimeout實(shí)現(xiàn)的。

到此這篇關(guān)于詳解JavaScript如何實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Promise對(duì)象的文章就介紹到這了,更多相關(guān)JavaScript實(shí)現(xiàn)Promise對(duì)象內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • js 輪播效果實(shí)例分享

    js 輪播效果實(shí)例分享

    本文主要分享了基于js實(shí)現(xiàn)的輪播效果的實(shí)例代碼,具有一定的參考價(jià)值,下面跟著小編一起來看下吧
    2016-12-12
  • 封裝好的js判斷操作系統(tǒng)與瀏覽器代碼分享

    封裝好的js判斷操作系統(tǒng)與瀏覽器代碼分享

    這篇文章主要介紹了封裝好的js判斷操作系統(tǒng)與瀏覽器代碼分享,對(duì)于我們做系統(tǒng)、瀏覽器兼容非常有幫助,需要的朋友可以參考下
    2015-01-01
  • JavaScript多線程詳解

    JavaScript多線程詳解

    雖然有越來越多的網(wǎng)站在應(yīng)用AJAX技術(shù)進(jìn)行開發(fā),但是構(gòu)建一個(gè)復(fù)雜的AJAX應(yīng)用仍然是一個(gè)難題。接下來小編給大家介紹JavaScript多線程,需要的朋友可以參考下
    2015-08-08
  • 微信小程序使用map組件實(shí)現(xiàn)獲取定位城市天氣或者指定城市天氣數(shù)據(jù)功能

    微信小程序使用map組件實(shí)現(xiàn)獲取定位城市天氣或者指定城市天氣數(shù)據(jù)功能

    這篇文章主要介紹了微信小程序使用map組件實(shí)現(xiàn)獲取定位城市天氣或者指定城市天氣數(shù)據(jù)功能,涉及微信小程序map組件結(jié)合微信API獲取天氣信息相關(guān)操作技巧,需要的朋友可以參考下
    2019-01-01
  • 利用Bootstrap實(shí)現(xiàn)表格復(fù)選框checkbox全選

    利用Bootstrap實(shí)現(xiàn)表格復(fù)選框checkbox全選

    Bootstrap相信應(yīng)該不用多介紹,來自 Twitter,是目前最受歡迎的前端框架。這篇文章主要給大家介紹了如何利用Bootstrap實(shí)現(xiàn)表格中的checkbox復(fù)選框全選效果,文中給出詳細(xì)的介紹及完整的實(shí)例代碼,相信對(duì)大家的理解和學(xué)習(xí)具有一定的參考借鑒價(jià)值,下面來一起看看吧。
    2016-12-12
  • js 為label標(biāo)簽和div標(biāo)簽賦值的方法

    js 為label標(biāo)簽和div標(biāo)簽賦值的方法

    這篇文章介紹了js 為label標(biāo)簽和div標(biāo)簽賦值的方法,有需要的朋友可以參考一下
    2013-08-08
  • three.js設(shè)置物體的縮放和旋轉(zhuǎn)代碼示例

    three.js設(shè)置物體的縮放和旋轉(zhuǎn)代碼示例

    最近在用three.js做三維模型的時(shí)候,需要通過鼠標(biāo)滑輪向前來控制視角朝鼠標(biāo)的位置放大,然后通過鼠標(biāo)滑輪向后將視角復(fù)原,這篇文章主要給大家介紹了關(guān)于three.js如何設(shè)置物體的縮放和旋轉(zhuǎn)的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • JavaScript中valueOf函數(shù)與toString方法深入理解

    JavaScript中valueOf函數(shù)與toString方法深入理解

    基本上,所有JS數(shù)據(jù)類型都擁有valueOf和toString這兩個(gè)方法,null除外。它們倆解決javascript值運(yùn)算與顯示的問題,本文將詳細(xì)介紹,有需要的朋友可以參考下
    2012-12-12
  • 基于javascript的無(wú)縫滾動(dòng)動(dòng)畫實(shí)現(xiàn)2

    基于javascript的無(wú)縫滾動(dòng)動(dòng)畫實(shí)現(xiàn)2

    這篇文章主要介紹了基于javascript的無(wú)縫滾動(dòng)動(dòng)畫實(shí)現(xiàn)2,文章通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 如何threejs利用indexeddb緩存加載glb模型

    如何threejs利用indexeddb緩存加載glb模型

    這篇文章主要介紹了如何threejs利用indexeddb緩存加載glb模型問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04

最新評(píng)論