摘要:其次要記錄狀態,判斷消息是否已被發布,如果未發布消息,則通過來注冊回調時,是將回調函數添加到內部的回調隊列中如果消息已發布,則通過來注冊回調時,直接將消息傳至回調函數,并執行規范中采用的狀態機制是可以轉化為或,并且只能轉化一次。
一點感悟
Promise 是編寫異步的另一種方式,鄙人愚見,它就是 Callback 的一種封裝
相比 Callback ,它有以下特點
Promise 將異步結果保存起來,可以隨時獲取
鏈式調用 then 方法會返回一個新的 Promise ,從而避免了回調地獄
決定一次異步有兩個環節
發起異步事件
處理異步結果
Promise 可以給一個異步事件注冊多個處理函數,舉個栗子,就像這樣
let p1 = new Promise((resolve) => { fs.readFile("./test.js", "utf8", (err, data) => { resolve(data) }) }) p1.then(data => console.log(data)) p1.then(data => console.log(data.toUpperCase()))
用 Callback 實現一樣的效果
用 callbacks 將所有注冊的函數保存
待異步事件返回結果,再遍歷 callbacks ,依次執行所有注冊的函數
就像這樣
let callbacks = [] function resolve(data){ callbacks.forEach(cb => cb(data)) } fs.readFile("./test.js", "utf8", (err, data) => { resolve(data) }) callbacks.push(data => console.log(data)) callbacks.push(data => console.log(data.toUpperCase()))
將上述代碼封裝一下
const fs = require("fs") class FakePromise { constructor(fn){ this.callbacks = [] resolve = resolve.bind(this) function resolve(data){ this.callbacks.forEach(cb => cb(data)) } fn(resolve) } then(onFulfilled){ this.callbacks.push(onFulfilled) } } let p1 = new FakePromise(resolve => { fs.readFile("./test.js", "utf8", (err, data) => { resolve(data) }) }) p1.then(data => console.log(data)) p1.then(data => console.log(data.toUpperCase()))
哈?是不是和真的 Promise 有點像
從發布-訂閱模式的角度來看:
FakePromise 中通過 .then(onFulfilled) 來訂閱消息,注冊處理異步結果的函數
通過 resolve(data) 來發布消息,觸發處理異步結果的函數去執行,發布的時機是異步事件完成時
延時 resolve先前的代碼存在一個問題,如果在執行 p1.then(data => console.log(data)) 之前,resolve(data) 就已經執行了,那么再通過 .then(onFulfilled) 注冊的處理異步結果的函數將永遠不會執行
為了避免這種情況,改造 resolve 函數,在其內部添加 setTimeout,從而保證那些注冊的處理函數是在下一個事件隊列中執行,就像這樣
function resolve(value) { setTimeout(() => { this.callbacks.forEach(cb => cb(value)) }, 0) }
通過延時執行 resolve 內部的函數,保證了先訂閱消息,再發布消息
但是 Promise 還有個額外的功能是在發布消息后,仍然可以訂閱消息,并且立即執行,就像這樣
const fs = require("fs") let p1 = new Promise(resolve => { fs.readFile("./test.js", "utf8", (err, data) => resolve(data)) }) p1.then(data => console.log(data)) setTimeout(function(){ p1.then(data => console.log(data.toUpperCase())) }, 5000)
5s之內,文件早已讀取成功,但是在5s之后,依然可以通過 .then 注冊處理事件,并且該事件會立即執行
先發布,再訂閱實現先發布,再訂閱的基礎是將消息保存下來。其次要記錄狀態,判斷消息是否已被發布,如果未發布消息,則通過 .then 來注冊回調時,是將回調函數添加到內部的回調隊列中;如果消息已發布,則通過 .then 來注冊回調時,直接將消息傳至回調函數,并執行
Promise 規范中采用的狀態機制是 pending、fulfilled、rejected
pending 可以轉化為 fulfilled 或 rejected ,并且只能轉化一次。
轉化為 fulfilled 和 rejected 后,狀態就不可再變
修改代碼如下
class FakePromise { constructor(fn) { this.value = null this.state = "pending" this.callbacks = [] resolve = resolve.bind(this) function resolve(value) { setTimeout(() => { this.value = value this.state = "fulfilled" this.callbacks.forEach(cb => cb(value)) }, 0) } fn(resolve) } then(onFulfilled) { if (this.state === "pending") { this.callbacks.push(onFulfilled) } else { onFulfilled(this.value) } } }
既然實現了先發布,再訂閱,那么 resolve 中的 setTimeout 是不是可以去掉了?
并不可以,因為人家正經的 Promise 是這樣的
let p1 = new Promise(resolve => { resolve("haha") }) p1.then(data => console.log(data)) p1.then(data => console.log(data.toUpperCase())) console.log("xixi") // xixi // haha // HAHA
只有保留 resolve 中 setTimeout 才能使 FakePromise 實現相同的效果
let p1 = new FakePromise(resolve => { resolve("haha") }) p1.then(data => console.log(data)) p1.then(data => console.log(data.toUpperCase())) console.log("xixi") // xixi // haha // HAHA
沒有 setTimeout 的輸出結果
// haha // HAHA // xixi鏈式 Promise
正經的 Promise 可以鏈式調用,從而避免了回調地獄
let p1 = new Promise(resolve => { fs.readFile("./test.js", "utf8", (err, data) => { resolve(data) }) }).then(res => { return new Promise(resolve => { fs.readFile("./main.js", "utf8", (err, data) => { resolve(data) }) }) }).then(res => { console.log(res) })
正經的 Promise 調用 then 方法會返回一個新的 Promise 對象
我們偽造的 FakePromise 并沒有實現這一功能,原來的 then 方法
... then(onFulfilled){ if (this.state === "pending") { this.callbacks.push(onFulfilled) } else { onFulfilled(this.value) } } ...
原來的 then 方法就是根據 state 判斷是注冊 onFulfilled 函數,還是執行 onFulfilled 函數
為了實現 FakePromise 的高仿,我們要改造 then 方法,使其返回一個新的 FakePromise ,為了方便區分,將返回的 FakePromise 取名為 SonFakePromise ,而先前調用 then 的對象為 FatherFakePromise
那么問題來了
那么構造這個 SonFakePromise 的函數參數是什么
這個 SonFakePromise 什么時候 resolve ?
首先,當構造一個新的 SonFakePromise 時,會將傳入的函數參數 fn 執行一遍,且這個函數有 resolve 參數
... then(onFulfilled){ if(this.state === "pending"){ this.callbacks.push(onFulfilled) let SonFakePromise = new FakePromise(function fn(resolve){ }) return SonFakePromise }else{ onFulfilled(this.value) let SonFakePromise = new FakePromise(function fn(resolve){ }) return SonFakePromise } } ...
現在的問題是這個 SonFakePromise 什么時候 resolve ?即構造函數中的函數參數 fn 如何定義
結合正經 Promise 的例子來看
let faherPromise = new Promise(resolve => { fs.readFile("./test.js", "utf8", (err, data) => { resolve(data) }) }).then(res => { return new Promise(resolve => { fs.readFile("./main.js", "utf8", (err, data) => { resolve(data) }) }) }).then(res => { console.log(res) }) // 等同于 let faherPromise = new Promise(resolve => { fs.readFile("./test.js", "utf8", (err, data) => { resolve(data) }) }) let sonPromise = faherPromise.then(function onFulfilled(res){ return new Promise(function fn(resolve){ fs.readFile("./main.js", "utf8", (err, data) => { resolve(data) }) }) }).then(res => { console.log(res) })
在例子中,onFulfilled 函數如下,且其執行后返回一個新的 Promise,暫時取名為 fulPromise
function onFulfilled(res) { return new Promise(function fn(resolve){ fs.readFile("./main.js", "utf8", (err, data) => { resolve(data) }) }) }
現在來分析一下,fatherPromise,sonPromise 和 fulPromise 這三者的關系
sonPromise 是調用 fatherPromise 的 then 方法返回的
而調用這個 then 方法需要傳入一個函數參數,取名為 retFulPromise
retFulPromise 函數執行的返回值 fulPromise
希望下面的代碼能有助于理解
let fatherPromise = new Promise(function fatherFn(fatherResolve){ fs.readFile("./test.js", "utf8", (err, data) => { fatherResolve(data) }) }) let sonPromise = fatherPromise.then(retFulPromise) function retFulPromise(res) { let fulPromise = new Promise(function fulFn(fulResolve){ fs.readFile("./main.js", "utf8", (err, data) => { fulResolve(data) }) }) return fulPromise }
fatherPromise 的狀態為 fulfilled 時,會執行 retFulPromise,其返回 fulPromise ,當這個 fulPromise 執行 fulResolve 時,即完成讀取 main.js 時, sonPromise 也會執行內部的 resolve
所以可以看成,sonPromise 的 sonResolve 函數,也被注冊到了 fulPromise 上
So,了解了整個流程,該怎么修改自己的 FakePromise 呢?
秀操作,考驗技巧的時候到了,將 sonResolve 的引用保存起來,注冊到 fulFakePromise 上
const fs = require("fs") class FakePromise { constructor(fn) { this.value = null this.state = "pending" this.callbacks = [] resolve = resolve.bind(this) function resolve(value) { setTimeout(() => { this.value = value this.state = "fulfilled" this.callbacks.forEach(cb => { let returnValue = cb.onFulfilled(value) if (returnValue instanceof FakePromise) { returnValue.then(cb.sonResolveRes) } }) }) } fn(resolve) } then(onFulfilled) { if (this.state === "pending") { let sonResolveRes = null let sonFakePromise = new FakePromise(function sonFn(sonResolve) { sonResolveRes = sonResolve }) this.callbacks.push({ sonFakePromise, sonResolveRes, onFulfilled }) return sonFakePromise } else { let value = onFulfilled(this.value) let sonResolveRes = null let sonFakePromise = new FakePromise(function sonFn(sonResolve) { sonResolveRes = sonResolve }) if (value instanceof FakePromise) { value.then(sonResolveRes) } return sonFakePromise } } }多角度測試
let fatherFakePromise = new FakePromise(resolve => { fs.readFile("./test.js", "utf8", (err, data) => { resolve(data) }) }) let sonFakePromise = fatherFakePromise.then(function onFulfilled(res) { return new FakePromise(function fn(resolve) { fs.readFile("./main.js", "utf8", (err, data) => { resolve(data) }) }) }).then(res => { console.log(res) })
let fatherFakePromise = new FakePromise(resolve => { fs.readFile("./test.js", "utf8", (err, data) => { resolve(data) }) }) setTimeout(function () { let sonFakePromise = fatherFakePromise.then(function onFulfilled(res) { return new FakePromise(function fn(resolve) { fs.readFile("./main.js", "utf8", (err, data) => { resolve(data) }) }) }).then(res => { console.log(res) }) }, 1000)
let fatherFakePromise = new FakePromise(resolve => { resolve("haha") }) let sonFakePromise = fatherFakePromise.then(function onFulfilled(res) { return new FakePromise(function fn(resolve) { fs.readFile("./main.js", "utf8", (err, data) => { resolve(data) }) }) }).then(res => { console.log(res) })
let fatherFakePromise = new FakePromise(resolve => { resolve("haha") }) setTimeout(function () { let sonFakePromise = fatherFakePromise.then(function onFulfilled(res) { return new FakePromise(function fn(resolve) { fs.readFile("./main.js", "utf8", (err, data) => { resolve(data) }) }) }).then(res => { console.log(res) }) }, 1000)參考資料
30分鐘,讓你徹底明白Promise原理
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/107249.html
摘要:據調研機構數據,年第三季度,全球智能手機芯片市場占有率中,聯發科力壓高通,歷史首次登頂全球第一。年月,聯發科發布全球首款十核處理器,以及它的升級版。聯發科本月表示,其最新的旗艦芯片將于明年第一季度發布,希望在農歷新年前推出。在被喊了一年的MTK YES后,聯發科終于迎來了自己的YES時刻。據調研機構Counterpoint數據,2020年第三季度,全球智能手機芯片市場占有率中,聯發科力壓高通...
摘要:以前實習的時候因為趕時間直接用的插件做了個折疊菜單,對于一個原生控來說還是更傾向于自己寫一個,畢竟為了個折疊菜單引入和有點太臃腫了。原版的效果其實也不難,主要是在開合的過程中添加了的過渡效果。 以前實習的時候因為趕時間直接用bootstrap的插件collapse.js做了個折疊菜單, 對于一個原生控來說還是更傾向于自己寫一個, 畢竟為了個折疊菜單引入jq和bootstrap有點太臃腫...
摘要:一反射機制概念程序運行時,允許改變程序結構或變量類型,這種語言稱為動態語言,如,是動態語言顯然,,不是動態語言,但是有著一個非常突出的動態相關機制。相關的為二獲取源頭重點打開權限所有類的對象其實都是的實例。 一、Java反射機制概念 程序運行時,允許改變程序結構或變量類型,這種語言稱為動態語言,如Python, Ruby是動態語言;顯然C++,Java,C#不是動態語言,但是JAVA有...
摘要:前言是一個優秀的前端庫,封裝了很多底層的實現,可以用來制作游戲,場景等。今年月新發布了,到今天為止已經更新到了。聲明本游戲來自于小站的官方教程,加入了一些個人的注釋,本文旨在幫助各位觀眾老爺快速上手。 前言 phaser是一個優秀的前端canvas庫,封裝了很多底層的實現,可以用來制作游戲,h5場景等。今年1月新發布了phaser3,到今天為止已經更新到了3.30。 聲明 本游戲來自于...
閱讀 1305·2021-11-22 09:34
閱讀 2169·2021-10-08 10:18
閱讀 1731·2021-09-29 09:35
閱讀 2463·2019-08-29 17:20
閱讀 2143·2019-08-29 15:36
閱讀 3409·2019-08-29 13:52
閱讀 784·2019-08-29 12:29
閱讀 1189·2019-08-28 18:10