国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

山寨一個 Promise

XFLY / 2720人閱讀

摘要:其次要記錄狀態,判斷消息是否已被發布,如果未發布消息,則通過來注冊回調時,是將回調函數添加到內部的回調隊列中如果消息已發布,則通過來注冊回調時,直接將消息傳至回調函數,并執行規范中采用的狀態機制是可以轉化為或,并且只能轉化一次。

一點感悟

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 規范中采用的狀態機制是 pendingfulfilledrejected

pending 可以轉化為 fulfilledrejected ,并且只能轉化一次。

轉化為 fulfilledrejected 后,狀態就不可再變

修改代碼如下

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

現在來分析一下,fatherPromisesonPromisefulPromise 這三者的關系

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年第三季度,全球智能手機芯片市場占有率中,聯發科力壓高通...

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

    摘要:以前實習的時候因為趕時間直接用的插件做了個折疊菜單,對于一個原生控來說還是更傾向于自己寫一個,畢竟為了個折疊菜單引入和有點太臃腫了。原版的效果其實也不難,主要是在開合的過程中添加了的過渡效果。 以前實習的時候因為趕時間直接用bootstrap的插件collapse.js做了個折疊菜單, 對于一個原生控來說還是更傾向于自己寫一個, 畢竟為了個折疊菜單引入jq和bootstrap有點太臃腫...

    IntMain 評論0 收藏0
  • 樂字節Java反射之一:反射概念與獲取反射源頭class

    摘要:一反射機制概念程序運行時,允許改變程序結構或變量類型,這種語言稱為動態語言,如,是動態語言顯然,,不是動態語言,但是有著一個非常突出的動態相關機制。相關的為二獲取源頭重點打開權限所有類的對象其實都是的實例。 一、Java反射機制概念 程序運行時,允許改變程序結構或變量類型,這種語言稱為動態語言,如Python, Ruby是動態語言;顯然C++,Java,C#不是動態語言,但是JAVA有...

    caikeal 評論0 收藏0
  • [phaser3入門探坑]使用phaser3制作山寨馬里奧

    摘要:前言是一個優秀的前端庫,封裝了很多底層的實現,可以用來制作游戲,場景等。今年月新發布了,到今天為止已經更新到了。聲明本游戲來自于小站的官方教程,加入了一些個人的注釋,本文旨在幫助各位觀眾老爺快速上手。 前言 phaser是一個優秀的前端canvas庫,封裝了很多底層的實現,可以用來制作游戲,h5場景等。今年1月新發布了phaser3,到今天為止已經更新到了3.30。 聲明 本游戲來自于...

    szysky 評論0 收藏0

發表評論

0條評論

XFLY

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<