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

JavaScript實(shí)現(xiàn)手寫promise的示例代碼

 更新時(shí)間:2023年04月12日 10:16:04   作者:Lvan的前端筆記  
promise?作為前端開發(fā)中常用的函數(shù),解決了?js?處理異步時(shí)回調(diào)地獄的問題,大家應(yīng)該也不陌生了,今天來學(xué)習(xí)一下?promise?的實(shí)現(xiàn)過程吧

背景

promise 作為前端開發(fā)中常用的函數(shù),解決了 js 處理異步時(shí)回調(diào)地獄的問題,大家應(yīng)該也不陌生了,今天來學(xué)習(xí)一下 promise 的實(shí)現(xiàn)過程,這樣可以加(面)深(試)理(要)解(考)。

需求

我們先來總結(jié)一下 promise 的特性:

使用:

const p1 = new Promise((resolve, reject) => {
  console.log('1');
  resolve('成功了');
})

console.log("2");

const p2 = p1.then(data => {
  console.log('3')
  throw new Error('失敗了')
})

const p3 = p2.then(data => {
  console.log('success', data)
}, err => {
  console.log('4', err)
})

以上的示例可以看出 promise 的一些特點(diǎn),也就是我們本次要做的事情:

  • 在調(diào)用 Promise 時(shí),會返回一個(gè) Promise 對象,包含了一些熟悉和方法(all/resolve/reject/then…)
  • 構(gòu)建 Promise 對象時(shí),需要傳入一個(gè) executor 函數(shù),接受兩個(gè)參數(shù),分別是resolve和reject,Promise 的主要業(yè)務(wù)流程都在 executor 函數(shù)中執(zhí)行。
  • 如果運(yùn)行在 excutor 函數(shù)中的業(yè)務(wù)執(zhí)行成功了,會調(diào)用 resolve 函數(shù);如果執(zhí)行失敗了,則調(diào)用 reject 函數(shù)。
  • promise 有三個(gè)狀態(tài):pending,fulfilled,rejected,默認(rèn)是 pending。只能從pending到rejected, 或者從pending到fulfilled,狀態(tài)一旦確認(rèn),就不會再改變;
  • promise 有一個(gè)then方法,接收兩個(gè)參數(shù),分別是成功的回調(diào) onFulfilled, 和失敗的回調(diào) onRejected。
// 三個(gè)狀態(tài):PENDING、FULFILLED、REJECTED
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
  constructor(executor) {
    // 默認(rèn)狀態(tài)為 PENDING
    this.status = PENDING;
    // 存放成功狀態(tài)的值,默認(rèn)為 undefined
    this.value = undefined;
    // 存放失敗狀態(tài)的值,默認(rèn)為 undefined
    this.reason = undefined;

    // 調(diào)用此方法就是成功
    let resolve = (value) => {
      // 狀態(tài)為 PENDING 時(shí)才可以更新狀態(tài),防止 executor 中調(diào)用了兩次 resovle/reject 方法
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
      }
    };

    // 調(diào)用此方法就是失敗
    let reject = (reason) => {
      // 狀態(tài)為 PENDING 時(shí)才可以更新狀態(tài),防止 executor 中調(diào)用了兩次 resovle/reject 方法
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
      }
    };

    try {
      // 立即執(zhí)行,將 resolve 和 reject 函數(shù)傳給使用者
      executor(resolve, reject);
    } catch (error) {
      // 發(fā)生異常時(shí)執(zhí)行失敗邏輯
      reject(error);
    }
  }

  // 包含一個(gè) then 方法,并接收兩個(gè)參數(shù) onFulfilled、onRejected
  then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }

    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
  }
}

調(diào)用一下:

const promise = new Promise((resolve, reject) => {
  resolve('成功');
}).then(
  (data) => {
    console.log('success', data)
  },
  (err) => {
    console.log('faild', err)
  }
)

這個(gè)時(shí)候我們很開心,但是別開心的太早,promise 是為了處理異步任務(wù)的,我們來試試異步任務(wù)好不好使:

const promise = new Promise((resolve, reject) => {
  // 傳入一個(gè)異步操作
  setTimeout(() => {
    resolve('成功');
  },1000);
}).then(
  (data) => {
    console.log('success', data)
  },
  (err) => {
    console.log('faild', err)
  }
)

發(fā)現(xiàn)沒有任何輸出,我們來分析一下為什么:

new Promise 執(zhí)行的時(shí)候,這時(shí)候異步任務(wù)開始了,接下來直接執(zhí)行 .then 函數(shù),then函數(shù)里的狀態(tài)目前還是 padding,所以就什么也沒執(zhí)行。

那么我們想想應(yīng)該怎么改呢?

我們想要做的就是,當(dāng)異步函數(shù)執(zhí)行完后,也就是觸發(fā) resolve 的時(shí)候,再去觸發(fā) .then 函數(shù)執(zhí)行并把 resolve 的參數(shù)傳給 then。

這就是典型的發(fā)布訂閱的模式,可以看我這篇文章。

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    // 存放成功的回調(diào)
    this.onResolvedCallbacks = [];
    // 存放失敗的回調(diào)
    this.onRejectedCallbacks= [];

    let resolve = (value) => {
      if(this.status ===  PENDING) {
        this.status = FULFILLED;
        this.value = value;
        // 依次將對應(yīng)的函數(shù)執(zhí)行
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    } 

    let reject = (reason) => {
      if(this.status ===  PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        // 依次將對應(yīng)的函數(shù)執(zhí)行
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    }

    try {
      executor(resolve,reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value)
    }

    if (this.status === REJECTED) {
      onRejected(this.reason)
    }

    if (this.status === PENDING) {
      // 如果promise的狀態(tài)是 pending,需要將 onFulfilled 和 onRejected 函數(shù)存放起來,等待狀態(tài)確定后,再依次將對應(yīng)的函數(shù)執(zhí)行
      this.onResolvedCallbacks.push(() => {
        onFulfilled(this.value)
      });

      // 如果promise的狀態(tài)是 pending,需要將 onFulfilled 和 onRejected 函數(shù)存放起來,等待狀態(tài)確定后,再依次將對應(yīng)的函數(shù)執(zhí)行
      this.onRejectedCallbacks.push(()=> {
        onRejected(this.reason);
      })
    }
  }
}

這下再進(jìn)行測試:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功');
  },1000);
}).then(
  (data) => {
    console.log('success', data)
  },
  (err) => {
    console.log('faild', err)
  }
)

1s 后輸出了:

說明成功啦

then的鏈?zhǔn)秸{(diào)用

這里就不分析細(xì)節(jié)了,大體思路就是每次 .then 的時(shí)候重新創(chuàng)建一個(gè) promise 對象并返回 promise,這樣下一個(gè) then 就能拿到前一個(gè) then 返回的 promise 了。

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

const resolvePromise = (promise2, x, resolve, reject) => {
  // 自己等待自己完成是錯(cuò)誤的實(shí)現(xiàn),用一個(gè)類型錯(cuò)誤,結(jié)束掉 promise  Promise/A+ 2.3.1
  if (promise2 === x) { 
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  // Promise/A+ 2.3.3.3.3 只能調(diào)用一次
  let called;
  // 后續(xù)的條件要嚴(yán)格判斷 保證代碼能和別的庫一起使用
  if ((typeof x === 'object' && x != null) || typeof x === 'function') { 
    try {
      // 為了判斷 resolve 過的就不用再 reject 了(比如 reject 和 resolve 同時(shí)調(diào)用的時(shí)候)  Promise/A+ 2.3.3.1
      let then = x.then;
      if (typeof then === 'function') { 
        // 不要寫成 x.then,直接 then.call 就可以了 因?yàn)?x.then 會再次取值,Object.defineProperty  Promise/A+ 2.3.3.3
        then.call(x, y => { // 根據(jù) promise 的狀態(tài)決定是成功還是失敗
          if (called) return;
          called = true;
          // 遞歸解析的過程(因?yàn)榭赡?promise 中還有 promise) Promise/A+ 2.3.3.3.1
          resolvePromise(promise2, y, resolve, reject); 
        }, r => {
          // 只要失敗就失敗 Promise/A+ 2.3.3.3.2
          if (called) return;
          called = true;
          reject(r);
        });
      } else {
        // 如果 x.then 是個(gè)普通值就直接返回 resolve 作為結(jié)果  Promise/A+ 2.3.3.4
        resolve(x);
      }
    } catch (e) {
      // Promise/A+ 2.3.3.2
      if (called) return;
      called = true;
      reject(e)
    }
  } else {
    // 如果 x 是個(gè)普通值就直接返回 resolve 作為結(jié)果  Promise/A+ 2.3.4  
    resolve(x)
  }
}

class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks= [];

    let resolve = (value) => {
      if(this.status ===  PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    } 

    let reject = (reason) => {
      if(this.status ===  PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    }

    try {
      executor(resolve,reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    //解決 onFufilled,onRejected 沒有傳值的問題
    //Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
    //因?yàn)殄e(cuò)誤的值要讓后面訪問到,所以這里也要跑出個(gè)錯(cuò)誤,不然會在之后 then 的 resolve 中捕獲
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
    // 每次調(diào)用 then 都返回一個(gè)新的 promise  Promise/A+ 2.2.7
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === FULFILLED) {
        //Promise/A+ 2.2.2
        //Promise/A+ 2.2.4 --- setTimeout
        setTimeout(() => {
          try {
            //Promise/A+ 2.2.7.1
            let x = onFulfilled(this.value);
            // x可能是一個(gè)proimise
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            //Promise/A+ 2.2.7.2
            reject(e)
          }
        }, 0);
      }

      if (this.status === REJECTED) {
        //Promise/A+ 2.2.3
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e)
          }
        }, 0);
      }

      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e)
            }
          }, 0);
        });

        this.onRejectedCallbacks.push(()=> {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0);
        });
      }
    });

    return promise2;
  }
}

Promise.all

核心就是有一個(gè)失敗則失敗,全成功才進(jìn)行 resolve

Promise.all = function(values) {
  if (!Array.isArray(values)) {
    const type = typeof values;
    return new TypeError(`TypeError: ${type} ${values} is not iterable`)
  }
  return new Promise((resolve, reject) => {
    let resultArr = [];
    let orderIndex = 0;
    const processResultByKey = (value, index) => {
      resultArr[index] = value;
      if (++orderIndex === values.length) {
          resolve(resultArr)
      }
    }
    for (let i = 0; i < values.length; i++) {
      let value = values[i];
      if (value && typeof value.then === 'function') {
        value.then((value) => {
          processResultByKey(value, i);
        }, reject);
      } else {
        processResultByKey(value, i);
      }
    }
  });
}

到此這篇關(guān)于JavaScript實(shí)現(xiàn)手寫promise的示例代碼的文章就介紹到這了,更多相關(guān)JavaScript手寫promise內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JS轉(zhuǎn)換HTML轉(zhuǎn)義符的方法

    JS轉(zhuǎn)換HTML轉(zhuǎn)義符的方法

    下面小編就為大家?guī)硪黄狫S轉(zhuǎn)換HTML轉(zhuǎn)義符的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-08-08
  • 使用JS判斷頁面是首次被加載還是刷新

    使用JS判斷頁面是首次被加載還是刷新

    這篇文章主要介紹了js判斷頁面是首次被加載還是刷新,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-05-05
  • JavaScript 輪播圖和自定義滾動條配合鼠標(biāo)滾輪分享代碼貼

    JavaScript 輪播圖和自定義滾動條配合鼠標(biāo)滾輪分享代碼貼

    本文給大家分享一段js輪播圖和自定義滾動條的代碼片段,布局和樣式小編沒給大家多介紹,大家可以根據(jù)個(gè)人需求優(yōu)化,具體實(shí)現(xiàn)代碼,大家可以參考下面代碼片段
    2016-10-10
  • javascript日期計(jì)算實(shí)例分析

    javascript日期計(jì)算實(shí)例分析

    這篇文章主要介紹了javascript日期計(jì)算,涉及javascript針對日期計(jì)算的相關(guān)技巧,這里需要注意索引的使用,需要的朋友可以參考下
    2015-06-06
  • ES6新特性之解構(gòu)、參數(shù)、模塊和記號用法示例

    ES6新特性之解構(gòu)、參數(shù)、模塊和記號用法示例

    這篇文章主要介紹了ES6新特性之解構(gòu)、參數(shù)、模塊和記號用法,結(jié)合實(shí)例形式分析了解構(gòu)、參數(shù)、模塊和記號的功能、用法及相關(guān)使用注意事項(xiàng),需要的朋友可以參考下
    2017-04-04
  • Knockoutjs的環(huán)境搭建教程

    Knockoutjs的環(huán)境搭建教程

    最近要在項(xiàng)目中使用Knockoutjs,因此今天就首先研究了一下Knockoutjs的環(huán)境搭建,并進(jìn)行了一個(gè)簡單的測試,需要的朋友可以了解下
    2012-11-11
  • 淺談Webpack打包優(yōu)化技巧

    淺談Webpack打包優(yōu)化技巧

    這篇文章主要介紹了淺談Webpack打包優(yōu)化,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-06-06
  • 詳述JavaScript實(shí)現(xiàn)繼承的幾種方式(推薦)

    詳述JavaScript實(shí)現(xiàn)繼承的幾種方式(推薦)

    這篇文章主要介紹了詳述JavaScript實(shí)現(xiàn)繼承的幾種方式(推薦)的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • 微信小程序button組件使用詳解

    微信小程序button組件使用詳解

    這篇文章主要為大家詳細(xì)介紹了微信小程序button組件的使用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 嘗試在讓script的type屬性等于text/html

    嘗試在讓script的type屬性等于text/html

    我們可以在script片斷中定義一個(gè)被JS調(diào)用的代碼,但代碼又不在頁面上顯示,這時(shí),我們可以使用下面的方法;當(dāng)script中的type等于text/html時(shí)我們可以做些什么呢?感興趣的朋友可以了解下啊
    2013-01-01

最新評論