日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

定时器和promise_分析 Promise 内部实现

發布時間:2023/12/19 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 定时器和promise_分析 Promise 内部实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在介紹Promise之前,首先我們舉一個栗子,看下面代碼

function success (message) {console.log(message)}function fail (message) {console.log(message)}function readFile (success, fail) {console.log('文件讀取啟動了...')setTimeout(() => {if ((+ new Date()) %2 === 0) {success('success:文件讀取成功了')} else {fail('fail:文件讀取失敗了')}}, 1000)}readFile(success, fail) // 執行 readFile 函數console.log('end...')

輸出結果:

【分析】

  • 這里我們定義了一個 readFile 函數,里面用一個 setTimeout 定時器模擬讀取文件得異步操作
  • 而后定時器里分情況調用 success 或 fail 函數 - 首先我們需要注意得是,在這里如果我們需要啟動定時器,必須執行 readFile() ,同時也必須把回調函數一起傳進去
  • 也就是說,如果 readFile 函數內部已經開始執行得時候 - 如果要有結果,此時必定有 success 和 fail 函數 - 簡單概括的話,就是在有結果輸出的情況下, readFile 函數內部元素壓入調用棧開始執行時, success 和 fail 回調函數 必定存在 --- 即已經定義好了
  • 至于為什么會用這個栗子開頭,請接著往下看....

初識 Promise

舉個栗子,看下面的代碼:

let file = new Promise ((resolve, reject) => {console.log('promise 讀取文件開始了...')setTimeout(() => {if ((+ new Date()) %2 === 0) {resolve('success:文件讀取成功了')} else {reject('fail:文件讀取失敗了')}}, 1000)})setTimeout(() => {// 3秒之后開始傳入回調函數file.then((res) => {console.log(res)}).catch((err) => {console.log(err)})}, 3000)console.log('end...')

輸出結果:

【分析】

  • 在第一行代碼,我們 new 了一個 Promise 對象 - 里面用了一個 1s 定時器 模擬 文件讀取 的異步操作
  • 看輸出結果,第一句輸出的是 Promise 內部的第一行代碼,也就是說,new Promise 對象時,內部已經開始執行了,
  • 1s之后,分情況調用 resolve 或 reject 函數 - 我們注意到,此時我們還沒有傳入回調函數,resolve 和 reject 只是我們 new Promise 時定義的形式參數
  • 3s 后,我們開始傳入回調函數,這時,我們在 3s 后傳入的回調函數執行了并且出現了結果

【思考】

我們回想一開始舉的例子

  • readFile 函數執行的時候,此時回調函數已經定義好了,并且作為參數傳入了 readFile 函數
  • new Promise 的時候,內部已經開始執行,此時我們并未傳入回調函數
  • 3s 之后,我們才傳入回調函數,神奇的是最后有結果輸出
  • 【提示】

    這樣一對比,你是不是發現了什么

  • Promise 一 創建對象 內部開始執行,此時我們沒有傳入回調函數
  • 未來的某段時間里,我們傳入回調函數,回調函數可能會執行并且可以取到結果
  • 【分析總結】

    • 在 Promise 內部,我們 new 的時候傳入的函數應該立即執行
    • 因為在未來的某段時間傳入回調函數可以取到結果,所以 Promise 內部應該有一個變量將 結果 緩存 起來了

    手動實現 Promise

    • 我們通過 new 創建 Promise 對象,所以 Promise 應該是一個函數,同時傳入一個函數作為參數,該函數立即執行
    • Promise 有 3 種狀態:pending、fulfilled、rejected(等待中、已成功、已失敗),并且狀態只能pending -> fulfilled (等待中 -> 已成功)或 pending -> rejected(等待中 -> 已失敗),改變之后狀態不能再次變化
    • 內部有變量存儲 失敗 或 成功 的結果,這里定義為存儲變量為 data
    • 同時 Promise 可以鏈式調用傳入回調函數,因此還存在變量保存回調函數集合,這里定義 保存成功的變量 onResolvedCallback,保存失敗的變量 onRejectedCallback
    let myPromise = (function () {// 定義變量保存狀態的名字const PENDING = 'pending' //初始狀態const FULFILLED = 'fulfilled' // 成功狀態const REJECTED = 'rejected' // 成功function Promise(executor) {this.status = PENDING // 定義變量保存 狀態,初始時為 pendingthis.data = undefined // 定義變量保存 結果this.onResolvedCallback = [] // resolve時的回調函數集合this.onRejectedCallback = [] // reject時的回調函數集合// 考慮到執行executor的過程中有可能出錯,所以我們用try/catch塊給包起來,并且在出錯后以catch捕獲到的值reject掉這個Promisetry {// 因為new Promise對象時傳入一個函數,該函數立即執行executor(resolve, reject) // 執行executor} catch (e) {reject(e)}}return Promise})()let p = new myPromise(function(resolve, reject) {setTimeout(() => {if ((+ new Date()) %2 === 0) {resolve('success:文件讀取成功了')} else {reject('fail:文件讀取失敗了')}}, 1000)})

    輸出結果

    • 因為我們在內部調用了 resolve 和 reject , 這兩個變量在函數內部我們沒有定義,所以出錯。那我們在內部定義的這兩個函數需要處理什么呢?
    • resolve 應該取狀態變為 成功 的 結果,reject 應該取狀態變為 失敗 的結果。

    繼續完善代碼

    let myPromise = (function () {// 定義變量保存狀態的名字const PENDING = 'pending' //初始狀態const FULFILLED = 'fulfilled' // 成功狀態const REJECTED = 'rejected' // 成功function Promise(executor) {this.status = PENDING // 定義變量保存 狀態,初始時為 pendingthis.data = undefined // 定義變量保存 結果this.onResolvedCallback = [] // resolve時的回調函數集合this.onRejectedCallback = [] // reject時的回調函數集合function resolve(result) { if (this.status === PENDING) { // 狀態只能是 pending, 才可以執行下面的代碼this.status = FULFILLED // 因為調用 resolve,所以是成功的,所以狀態變為 fulfilledthis.data = result // 保存 成功 的結果// 開始給 誰想要當前 成功的 結果,就執行他們的函數,并且將 成功 的結果傳給他們this.onResolvedCallback.forEach(cb => cb(this.data))}}function reject(reason) {// 同上if (this.status === PENDING) {this.status = REJECTEDthis.data = reasonthis.onRejectedCallback.forEach(cb => cb(this.data))}}// 考慮到執行executor的過程中有可能出錯,所以我們用try/catch塊給包起來,并且在出錯后以catch捕獲到的值reject掉這個Promisetry {// 因為new Promise對象時傳入一個函數,該函數立即執行executor(resolve, reject) // 執行executor} catch (e) {reject(e)}}return Promise})()let p = new myPromise(function (resolve, reject) {setTimeout(() => {if ((+ new Date()) % 2 === 0) {resolve('success:文件讀取成功了')} else {reject('fail:文件讀取失敗了')}}, 1000)})

    到這一步,我們剛才分析的內容大部分完成了 如果我們 MDN 上搜索 Promise

    MDN上Promise介紹
    • 我們發現 then、catch、finally 是 Promise 類的實例方法
    • all、race、reject 、resolve 是 Promise 類的靜態方法
    • 所以我們根據分析,可以得出如下的 結構
    Promise.prototype.then = function() {} Promise.prototype.catch = function() {} Promise.all = function() {} Promise.race = function() {} Promise.reject = function() {} Promise.resolve = function() {}

    Promise.then 方法實現

    • Promise.then 接受兩個參數,一個為 成功狀態 的回調函數,一個為 失敗狀態 的回調函數
    • 同時我們可以不傳回調函數,這時直接取 上一個處理的結果,不做處理,直接傳遞到 下一個

    講解

    • 首先,then 可以鏈式調用,所以每次都需要返回一個 Promise 對象
    Promise.prototype.then = function (onFulfilled, onRejected) {return new Promise(function (resolve, reject) {}}
    • 我們通過前面的分析,已經知道 then 里面的回調函數可以 在不同時間段給出,此時,當前這個 promise 可能出現 3 種狀態 (等待中、已成功、已失敗),所以分 3 種情況 處理
    Promise.prototype.then = function (onFulfilled, onRejected) {return new Promise(function (resolve, reject) {if (self.status == FULFILLED) { // 已成功}if (self.status == REJECTED) { // 已失敗}if (self.status === PENDING) { // 等待中}})}
    • 接下來我們分析,在這 3 種情況下應該做什么

    已成功

    • 因為 狀態已經變成了 已成功,所以存儲了 成功的結果,我們將 成功的結果 作為參數傳遞給 onFulfilled 回調函數并執行
    • 因為 返回的 promise 的狀態由 onFulfilled 回調函數執行的結果決定
    • 所以我們開始分析 onFulfilled 可能有什么樣的結果
  • 如果拋出異常,return 的 Promise 就會失敗,結果 就是 error
  • 如果回調函數返回不是 Promise,return 的 Promise 就會成功,value 就是返回的值
  • 如果回調函數返回是 Promise,return 的 Promise 結果就是這個 Promise 的結果
    • 上面 3 種 結果是什么意思呢?我們以下面的栗子來說明
    var p1 = new Promise()// 1. 如果拋出異常,return 的 Promise 就會失敗,結果 就是 error p1.then(() => { // 就是有人會這么直接拋出異常,所以 p1 return 的 Promise 狀態應該是 已失敗throw 233 })// 2. 如果回調函數返回不是 Promise,return 的 Promise 就會成功,value 就是返回的值 p1.then(() => { // 直接 return 一個 值,因為沒有拋出異常,所以不是 已失敗狀態,同時 因為有了結果,所以不是 等待中,最后return 的 Promise 只能是 已成功 狀態,return 1433 }) // 3. 如果回調函數返回是 Promise,return 的 Promise 結果就是這個 Promise 的結果 p1.then(() => { // 因為 返回 了 一個 Promise,所以當前狀態無法確定,只能通過 返回的 這個 Promise 的最后狀態來確定return new Promise(() => {}) })
    • 根據 分析,我們初步實現 已成功 的 內容,因為要用到 Promise 的 存儲的結果 data,所以我們定義一個變量self= this
    Promise.prototype.then = function (onFulfilled, onRejected) { let self = thisreturn new Promise(function (resolve, reject) {if (self.status == FULFILLED) {// 已成功setTimeout(() => {try {// 獲取 回調函數執行的結果var _result = onFulfilled(self.data)if (_result instanceof Promise) {// 3. 如果回調函數返回是 Promise,return 的 Promise 結果就是這個 Promise 的結果_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {// 2. 如果回調函數返回不是 Promise,return 的 Promise 就會成功,value 就是返回的值resolve(_result)}} catch (e) {// 1. 如果拋出異常,return 的 Promise 就會失敗,結果 就是 errorreject(e)}})}if (self.status == REJECTED) {// 已失敗}if (self.status === PENDING) {// 等待中}})}

    問題:為什么 then回調函數必須設置為 setTimeout 的回調?

    setTimeout(() => { // 保證返回的 Promise 是異步try {var _result = onFulfilled(self.data)if (_result instanceof Promise) {_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}})
    • 保證返回的 Promise 是異步
    • (這個部分,下次更新)

    已失敗

    • 分析的思路和結果與上面的差不多,不做過多分析
    • 需要注意的是此時的 _result = onRejected(self.data) ,調用失敗的回調函數

    等待中

    • 因為當前 Promise 的狀態是 進行中, 所以我們無法得知最后的狀態是
    • 我們得等到 Promise 的 最后狀態確定了 ,才能 知道 是調用 onFulfilled 還是 onRejected
    • 這時候我們通過發布訂閱模式,將當前傳入的回調函數都保存起來,等到有了結果再進行通知,并把最后狀態的結果傳給回調函數
    Promise.prototype.then = function (onFulfilled, onRejected) {let self = thisreturn new Promise(function (resolve, reject) {if (self.status == FULFILLED) {// 已成功setTimeout(() => {try {// 獲取 回調函數執行的結果var _result = onFulfilled(self.data)if (_result instanceof Promise) {// 3. 如果回調函數返回是 Promise,return 的 Promise 結果就是這個 Promise 的結果_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {// 2. 如果回調函數返回不是 Promise,return 的 Promise 就會成功,value 就是返回的值resolve(_result)}} catch (e) {// 1. 如果拋出異常,return 的 Promise 就會失敗,結果 就是 errorreject(e)}})}if (self.status == REJECTED) { // 已失敗setTimeout(() => {try {var _result = onRejected(self.data)if (_result instanceof Promise) {_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}})}if (self.status === PENDING) { // 等待中self.onResolvedCallback.push(function () {setTimeout(() => {try {var _result = onFulfilled(self.data)if (_result instanceof Promise) {_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}})})self.onRejectedCallback.push(function () {setTimeout(() => {try {var _result = onRejected(self.data)if (_result instanceof Promise) {_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}})})}})}
    • 我們發現有 四個 地方的內容我們重復寫了 四遍,所以提取出來就變成了
    Promise.prototype.then = function (onFulfilled, onRejected) {let self = thisreturn new Promise(function (resolve, reject) {// 根據指定回調函數處理,根據執行結果,改變 return 的 promise 的狀態function handle(callback) {try {var _result = callback(self.data)if (_result instanceof Promise) {_result.then(res => {resolve(res)}, err => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}}if (self.status == FULFILLED) {setTimeout(() => {handle(onFulfilled)})}if (self.status == REJECTED) {// 如果當前是 resolved 狀態,異步執行 onRejected 并改變 return 的 promise 狀態setTimeout(() => {handle(onRejected)})}if (self.status === PENDING) {self.onResolvedCallback.push(function () {setTimeout(() => {handle(onFulfilled)})})self.onRejectedCallback.push(function () {setTimeout(() => {handle(onRejected)})})}})}

    透傳

    • 由于我們調用 .then 方法時,可以一個回調函數都不傳,此時應當不對最后狀態的結果進行處理,直接傳遞到下一個 Promise
    • 請始終記得,只要沒有手動對狀態確定的 Promise 的結果進行 過處理,Promise 的狀態就不會再改變
    Promise.prototype.then = function (onFulfilled, onRejected) {onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : (value) => valueonRejected = typeof onRejected == 'function' ? onRejected : (reason) => { throw reason }let self = thisreturn new Promise(function (resolve, reject) {// 根據指定回調函數處理,根據執行結果,改變 return 的 promise 的狀態function handle(callback) {try {var _result = callback(self.data)if (_result instanceof Promise) {_result.then(res => {resolve(res)}, err => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}}if (self.status == FULFILLED) {setTimeout(() => {handle(onFulfilled)})}if (self.status == REJECTED) {// 如果當前是 resolved 狀態,異步執行 onRejected 并改變 return 的 promise 狀態setTimeout(() => {handle(onRejected)})}if (self.status === PENDING) {self.onResolvedCallback.push(function () {setTimeout(() => {handle(onFulfilled)})})self.onRejectedCallback.push(function () {setTimeout(() => {handle(onRejected)})})}})}
    • 通過以上分析,我們已經實現了 .then 方法

    Promise.catch 方法實現

    • catch 方法,傳入一個失敗的回調函數,因為需要獲取 失敗的結果,所以我們直接調用當前 Promise 的 then 方法來獲取 失敗的結果
    • 同時調用了 .catch 之后,還是返回一個 Promise 對象,并且 下一個 Promise 的狀態由 .catch 的回調函數 返回值確定
    • 在這里人們會存在的一個誤解是:調用 .catch 之后,以后的 Promise 狀態都是 已失敗,這是錯誤的
    • 你需要時刻記得的是:只要你沒有用回調函數處理過 已失敗 的結果,之后的 Promise 狀態就是 已失敗,只要你 用回調函數處理了 已失敗的結果,之后的 Promise 狀態就根據你 回調函數的 結果來 確定。
    Promise.prototype.catch = function (onRejected) {return this.then(undefined, onRejected)}

    Promise.all 方法實現

    • Promise.all 方法傳入一個數組,當數組中有一個 結果是 已失敗 時,最后結果就是 已失敗,只有當數組中的 所有 結果都是 成功時,該方法才會被確定為 已成功 的狀態
    • 已成功 的 結果是一個數組
    • 需要注意的是,已成功 的 結果數組,和 Promise.all 傳入的數組是相對應得
    • 也就是說,傳入的第 2 個 是 已成功 數組 的 第 2 個 結果
    • 這里我們可以借鑒 我們常遇到的 保證多個ajax 請求都返回了結果 的解決 辦法。
    • 我們通過一個變量(successNum)來保存 已成功 的數量,如果最后該變量的值 和 傳入的數組 長度 一樣,那就說明 傳入的 數組 所有結果都是 已成功
    • 遇到 一個執行結果是 已失敗,直接返回 已失敗 的狀態

    【分析】傳入的數組可能的情況

    • [1, '233', new Promise()]
    • 一是 字符串或數字
    • 二是 Promise 對象
    Promise.all = function (promises) {let len = promises.lengthlet successNum = 0let _result = []return new Promise((resolve, reject) => {promises.forEach((p, i) => {try {// 針對 Promise 對象if (p instanceof Promise) {p.then((res) => {successNum++_result[i] = resif (successNum === len) {resolve(_result)}},(err) => {reject(err)})} else {// 非 Promise 對象successNum++_result[i] = pif (successNum === len) {resolve(_result)}}} catch (err) {reject(err)}})}) }

    Promise.race 方法實現

    • Promise.race , 傳入的是一個數組,誰先有結果,返回的 Promise 的狀態就是該結果的 狀態
    • 該方法和 Promise.all 相比,不需要變量 保存 誰已經執行了,只需要遇到哪個執行完成了,直接返回和其結果一樣的狀態
    Promise.race = function (promises) {return new Promise((resolve, reject) => {promises.forEach((p, i) => {try {if (p instanceof Promise) {p.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(p)}} catch (err) {reject(err)}})}) }

    Promise.reject 方法實現

    • 該方法返回一個 已失敗 狀態 的 Promise
    • 直接通過新建一個 Promise 對象,只傳入第二個參數,返回一個 已失敗 的 Promise
    Promise.reject = function (rejected) {return new Promise((undefined, reject) => {reject(rejected)}) }

    Promise.resolve 方法實現

    • 該方法返回一個 已成功 狀態的 Promise
    • 我們同樣通過直接返回一個 Promise 對象來 獲取 返回成功的 狀態
    • 這里需要注意的是:因為是返回 一個 已成功 的狀態,但是我們并不能保證 用戶傳入的 結果最后就是 已成功 的狀態
    • 該方法和 Promise.reject 不同
    • 用戶如果傳入了一個 Promise 對象 作為參數,那我們應該 取 傳入的參數結果和狀態作為 當前 Promise 最后返回的結果和狀態
    Promise.resolve = function (result) {return new Promise((resolve, reject) => {try {// 如果傳入的是一個 Promise 類型的變量,取它的狀態和結果作為 Promise.resolve 的狀態和結果if (result instanceof Promise) {result.then((res) => {resolve(res)},(err) => {reject(err)})}resolve(result)} catch (e) {reject(e)}}) }

    結果

    • 發一個最終版和測試用例
    const _Promise = (function () {const PENDING = 'pending' //初始狀態const FULFILLED = 'fulfilled' // 成功狀態const REJECTED = 'rejected' // 失敗狀態function Promise(executor) {var self = thisself.status = PENDINGself.data = undefinedself.onResolvedCallback = []self.onRejectedCallback = []function resolve(result) {if (self.status === PENDING) {self.status = FULFILLEDself.data = resultself.onResolvedCallback.forEach((cb) => cb(self.data))}}function reject(reason) {if (self.status === PENDING) {self.status = REJECTEDself.data = reasonself.onRejectedCallback.forEach((cb) => cb(self.data))}}try {executor(resolve, reject)} catch (e) {reject(e)}}Promise.prototype.then = function (onFulfilled, onRejected) {onFulfilled =typeof onFulfilled == 'function' ? onFulfilled : (value) => valueonRejected =typeof onRejected == 'function'? onRejected: (reason) => {throw reason}let self = thisreturn new Promise(function (resolve, reject) {// 根據指定回調函數處理,根據執行結果,改變 return 的 promise 的狀態function handle(callback) {try {var _result = callback(self.data)if (_result instanceof Promise) {_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}}if (self.status == FULFILLED) {setTimeout(() => {handle(onFulfilled)})}if (self.status == REJECTED) {// 如果當前是 resolved 狀態,異步執行 onRejected 并改變 return 的 promise 狀態setTimeout(() => {handle(onRejected)})}if (self.status === PENDING) {self.onResolvedCallback.push(function () {setTimeout(() => {handle(onFulfilled)})})self.onRejectedCallback.push(function () {setTimeout(() => {handle(onRejected)})})}})}Promise.prototype.catch = function (onRejected) {return this.then(undefined, onRejected)}Promise.all = function (promises) {let len = promises.lengthlet successNum = 0let _result = []return new Promise((resolve, reject) => {promises.forEach((p, i) => {try {if (p instanceof Promise) {p.then((res) => {successNum++_result[i] = resif (successNum === len) {resolve(_result)}},(err) => {reject(err)})} else {successNum++_result[i] = pif (successNum === len) {resolve(_result)}}} catch (err) {reject(err)}})})}Promise.race = function (promises) {return new Promise((resolve, reject) => {promises.forEach((p, i) => {try {if (p instanceof Promise) {p.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(p)}} catch (err) {reject(err)}})})}Promise.reject = function (rejected) {return new Promise((undefined, reject) => {reject(rejected)})}Promise.resolve = function (result) {return new Promise((resolve, reject) => {try {if (result instanceof Promise) {result.then((res) => {resolve(res)},(err) => {reject(err)})}resolve(result)} catch (e) {reject(e)}})}return Promise})()let p1 = new _Promise((resolve, reject) => {setTimeout(() => {resolve('p1 ---> resolve')}, 1000)})let p2 = new _Promise((resolve, reject) => {setTimeout(() => {resolve('p2 ---> resolve')}, 500)})let p3 = new _Promise((resolve, reject) => {setTimeout(() => {reject('p3 ---> reject')}, 1500)})_Promise.all([p1, p2, 120, 'hello', p3]).then((res) => {console.log('resolve', res)}).then(() => {},(err) => {console.log('err', err)throw ''}).then((res) => {console.log('res', res)},(err) => {console.log('err', err)})

    【輸出結果】

    err p3 ---> reject err

    總結

    以上是生活随笔為你收集整理的定时器和promise_分析 Promise 内部实现的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。