成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

山寨一個(gè) Promise

XFLY / 2722人閱讀

摘要:其次要記錄狀態(tài),判斷消息是否已被發(fā)布,如果未發(fā)布消息,則通過(guò)來(lái)注冊(cè)回調(diào)時(shí),是將回調(diào)函數(shù)添加到內(nèi)部的回調(diào)隊(duì)列中如果消息已發(fā)布,則通過(guò)來(lái)注冊(cè)回調(diào)時(shí),直接將消息傳至回調(diào)函數(shù),并執(zhí)行規(guī)范中采用的狀態(tài)機(jī)制是可以轉(zhuǎn)化為或,并且只能轉(zhuǎn)化一次。

一點(diǎn)感悟

Promise 是編寫(xiě)異步的另一種方式,鄙人愚見(jiàn),它就是 Callback 的一種封裝

相比 Callback ,它有以下特點(diǎn)

Promise 將異步結(jié)果保存起來(lái),可以隨時(shí)獲取

鏈?zhǔn)秸{(diào)用 then 方法會(huì)返回一個(gè)新的 Promise ,從而避免了回調(diào)地獄

決定一次異步有兩個(gè)環(huán)節(jié)

發(fā)起異步事件

處理異步結(jié)果

Promise 可以給一個(gè)異步事件注冊(cè)多個(gè)處理函數(shù),舉個(gè)栗子,就像這樣

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 實(shí)現(xiàn)一樣的效果

用 callbacks 將所有注冊(cè)的函數(shù)保存

待異步事件返回結(jié)果,再遍歷 callbacks ,依次執(zhí)行所有注冊(cè)的函數(shù)

就像這樣

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 有點(diǎn)像

從發(fā)布-訂閱模式的角度來(lái)看:

FakePromise 中通過(guò) .then(onFulfilled) 來(lái)訂閱消息,注冊(cè)處理異步結(jié)果的函數(shù)

通過(guò) resolve(data) 來(lái)發(fā)布消息,觸發(fā)處理異步結(jié)果的函數(shù)去執(zhí)行,發(fā)布的時(shí)機(jī)是異步事件完成時(shí)

延時(shí) resolve

先前的代碼存在一個(gè)問(wèn)題,如果在執(zhí)行 p1.then(data => console.log(data)) 之前,resolve(data) 就已經(jīng)執(zhí)行了,那么再通過(guò) .then(onFulfilled) 注冊(cè)的處理異步結(jié)果的函數(shù)將永遠(yuǎn)不會(huì)執(zhí)行

為了避免這種情況,改造 resolve 函數(shù),在其內(nèi)部添加 setTimeout,從而保證那些注冊(cè)的處理函數(shù)是在下一個(gè)事件隊(duì)列中執(zhí)行,就像這樣

function resolve(value) {
    setTimeout(() => {
        this.callbacks.forEach(cb => cb(value))
    }, 0)
}

通過(guò)延時(shí)執(zhí)行 resolve 內(nèi)部的函數(shù),保證了先訂閱消息,再發(fā)布消息

但是 Promise 還有個(gè)額外的功能是在發(fā)布消息后,仍然可以訂閱消息,并且立即執(zhí)行,就像這樣

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之內(nèi),文件早已讀取成功,但是在5s之后,依然可以通過(guò) .then 注冊(cè)處理事件,并且該事件會(huì)立即執(zhí)行

先發(fā)布,再訂閱

實(shí)現(xiàn)先發(fā)布,再訂閱的基礎(chǔ)是將消息保存下來(lái)。其次要記錄狀態(tài),判斷消息是否已被發(fā)布,如果未發(fā)布消息,則通過(guò) .then 來(lái)注冊(cè)回調(diào)時(shí),是將回調(diào)函數(shù)添加到內(nèi)部的回調(diào)隊(duì)列中;如果消息已發(fā)布,則通過(guò) .then 來(lái)注冊(cè)回調(diào)時(shí),直接將消息傳至回調(diào)函數(shù),并執(zhí)行

Promise 規(guī)范中采用的狀態(tài)機(jī)制是 pending、fulfilledrejected

pending 可以轉(zhuǎn)化為 fulfilledrejected ,并且只能轉(zhuǎn)化一次。

轉(zhuǎn)化為 fulfilledrejected 后,狀態(tài)就不可再變

修改代碼如下

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)
        }
    }
}

既然實(shí)現(xiàn)了先發(fā)布,再訂閱,那么 resolve 中的 setTimeout 是不是可以去掉了?

并不可以,因?yàn)槿思艺?jīng)的 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 實(shí)現(xiàn)相同的效果

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

沒(méi)有 setTimeout 的輸出結(jié)果

// haha
// HAHA
// xixi
鏈?zhǔn)?Promise

正經(jīng)的 Promise 可以鏈?zhǔn)秸{(diào)用,從而避免了回調(diào)地獄

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)
})

正經(jīng)的 Promise 調(diào)用 then 方法會(huì)返回一個(gè)新的 Promise 對(duì)象

我們偽造的 FakePromise 并沒(méi)有實(shí)現(xiàn)這一功能,原來(lái)的 then 方法

...
    then(onFulfilled){
        if (this.state === "pending") {
            this.callbacks.push(onFulfilled)
        } else {
            onFulfilled(this.value)
        }
    }
...

原來(lái)的 then 方法就是根據(jù) state 判斷是注冊(cè) onFulfilled 函數(shù),還是執(zhí)行 onFulfilled 函數(shù)

為了實(shí)現(xiàn) FakePromise 的高仿,我們要改造 then 方法,使其返回一個(gè)新的 FakePromise ,為了方便區(qū)分,將返回的 FakePromise 取名為 SonFakePromise ,而先前調(diào)用 then 的對(duì)象為 FatherFakePromise

那么問(wèn)題來(lái)了

那么構(gòu)造這個(gè) SonFakePromise 的函數(shù)參數(shù)是什么

這個(gè) SonFakePromise 什么時(shí)候 resolve ?

首先,當(dāng)構(gòu)造一個(gè)新的 SonFakePromise 時(shí),會(huì)將傳入的函數(shù)參數(shù) fn 執(zhí)行一遍,且這個(gè)函數(shù)有 resolve 參數(shù)

...
    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
      }
    }
...

現(xiàn)在的問(wèn)題是這個(gè) SonFakePromise 什么時(shí)候 resolve ?即構(gòu)造函數(shù)中的函數(shù)參數(shù) fn 如何定義

結(jié)合正經(jīng) Promise 的例子來(lái)看

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 函數(shù)如下,且其執(zhí)行后返回一個(gè)新的 Promise,暫時(shí)取名為 fulPromise

function onFulfilled(res) {
  return new Promise(function fn(resolve){
    fs.readFile("./main.js", "utf8", (err, data) => {
      resolve(data)
    })
  })
}

現(xiàn)在來(lái)分析一下,fatherPromise,sonPromisefulPromise 這三者的關(guān)系

sonPromise 是調(diào)用 fatherPromise 的 then 方法返回的

而調(diào)用這個(gè) then 方法需要傳入一個(gè)函數(shù)參數(shù),取名為 retFulPromise

retFulPromise 函數(shù)執(zhí)行的返回值 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 的狀態(tài)為 fulfilled 時(shí),會(huì)執(zhí)行 retFulPromise,其返回 fulPromise ,當(dāng)這個(gè) fulPromise 執(zhí)行 fulResolve 時(shí),即完成讀取 main.js 時(shí), sonPromise 也會(huì)執(zhí)行內(nèi)部的 resolve

所以可以看成,sonPromise 的 sonResolve 函數(shù),也被注冊(cè)到了 fulPromise 上

So,了解了整個(gè)流程,該怎么修改自己的 FakePromise 呢?

秀操作,考驗(yàn)技巧的時(shí)候到了,將 sonResolve 的引用保存起來(lái),注冊(cè)到 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
        }
    }
}
多角度測(cè)試
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原理

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/107249.html

相關(guān)文章

  • 歷史首次!聯(lián)發(fā)科打敗高通成為全球第一,憑什么?

    摘要:據(jù)調(diào)研機(jī)構(gòu)數(shù)據(jù),年第三季度,全球智能手機(jī)芯片市場(chǎng)占有率中,聯(lián)發(fā)科力壓高通,歷史首次登頂全球第一。年月,聯(lián)發(fā)科發(fā)布全球首款十核處理器,以及它的升級(jí)版。聯(lián)發(fā)科本月表示,其最新的旗艦芯片將于明年第一季度發(fā)布,希望在農(nóng)歷新年前推出。在被喊了一年的MTK YES后,聯(lián)發(fā)科終于迎來(lái)了自己的YES時(shí)刻。據(jù)調(diào)研機(jī)構(gòu)Counterpoint數(shù)據(jù),2020年第三季度,全球智能手機(jī)芯片市場(chǎng)占有率中,聯(lián)發(fā)科力壓高通...

    Tecode 評(píng)論0 收藏0
  • 原生JS模擬Bootstrap中的折疊(Collapse)插件

    摘要:以前實(shí)習(xí)的時(shí)候因?yàn)橼s時(shí)間直接用的插件做了個(gè)折疊菜單,對(duì)于一個(gè)原生控來(lái)說(shuō)還是更傾向于自己寫(xiě)一個(gè),畢竟為了個(gè)折疊菜單引入和有點(diǎn)太臃腫了。原版的效果其實(shí)也不難,主要是在開(kāi)合的過(guò)程中添加了的過(guò)渡效果。 以前實(shí)習(xí)的時(shí)候因?yàn)橼s時(shí)間直接用bootstrap的插件collapse.js做了個(gè)折疊菜單, 對(duì)于一個(gè)原生控來(lái)說(shuō)還是更傾向于自己寫(xiě)一個(gè), 畢竟為了個(gè)折疊菜單引入jq和bootstrap有點(diǎn)太臃腫...

    IntMain 評(píng)論0 收藏0
  • 樂(lè)字節(jié)Java反射之一:反射概念與獲取反射源頭class

    摘要:一反射機(jī)制概念程序運(yùn)行時(shí),允許改變程序結(jié)構(gòu)或變量類(lèi)型,這種語(yǔ)言稱(chēng)為動(dòng)態(tài)語(yǔ)言,如,是動(dòng)態(tài)語(yǔ)言顯然,,不是動(dòng)態(tài)語(yǔ)言,但是有著一個(gè)非常突出的動(dòng)態(tài)相關(guān)機(jī)制。相關(guān)的為二獲取源頭重點(diǎn)打開(kāi)權(quán)限所有類(lèi)的對(duì)象其實(shí)都是的實(shí)例。 一、Java反射機(jī)制概念 程序運(yùn)行時(shí),允許改變程序結(jié)構(gòu)或變量類(lèi)型,這種語(yǔ)言稱(chēng)為動(dòng)態(tài)語(yǔ)言,如Python, Ruby是動(dòng)態(tài)語(yǔ)言;顯然C++,Java,C#不是動(dòng)態(tài)語(yǔ)言,但是JAVA有...

    caikeal 評(píng)論0 收藏0
  • [phaser3入門(mén)探坑]使用phaser3制作山寨馬里奧

    摘要:前言是一個(gè)優(yōu)秀的前端庫(kù),封裝了很多底層的實(shí)現(xiàn),可以用來(lái)制作游戲,場(chǎng)景等。今年月新發(fā)布了,到今天為止已經(jīng)更新到了。聲明本游戲來(lái)自于小站的官方教程,加入了一些個(gè)人的注釋?zhuān)疚闹荚趲椭魑挥^眾老爺快速上手。 前言 phaser是一個(gè)優(yōu)秀的前端canvas庫(kù),封裝了很多底層的實(shí)現(xiàn),可以用來(lái)制作游戲,h5場(chǎng)景等。今年1月新發(fā)布了phaser3,到今天為止已經(jīng)更新到了3.30。 聲明 本游戲來(lái)自于...

    szysky 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<