ES6記錄異步函數(shù)的執(zhí)行時間詳解
calc
calc
是一個我們想要做剖析(性能分析)的異步函數(shù)。按照慣例,它的最后一個參數(shù)是一個callback
。我們像這樣使用 calc
:
calc(arg, (err, res) => console.log(err || res))
或許,最簡單的對 calc
這樣的函數(shù)來剖析性能的方法是,增加一個計時邏輯到我們需要分析的地方:
const t0 = Date.now() calc(arg, (err, res) => { const t1 = Date.now() console.log(`Log: time: ${t1 = t0}`) console.log(err || res) })
但是,這不是一個可復(fù)用的解決方案。每一次我們想要對一個函數(shù)計時,我們得引入一個 t0 在外層作用域并且改變 callback
來測量和記錄時間。
對我來說理想的方式是能夠僅僅通過包裝一個異步函數(shù)就能夠?qū)λM行計時:
timeIt(calc)(arg, (err, res) => console.log(err || res))
timeIt
需要能夠很好地對每一個異步函數(shù)完成剖析和記錄執(zhí)行時間。
注意到 timeIt(calc)
有與原始的 calc 函數(shù)同樣的函數(shù)簽名,即它們接受同樣的參數(shù)和返回同樣的值,它只是增加了一個特性到 cale 上(能夠被記錄時間的特性)。
calc 和 timeIt(calc) 在任意時刻可以相互替代。
timeIt
本身是一個高階函數(shù),因為它接受一個函數(shù)并返回一個函數(shù)。在我們的例子里,它接受 calc 異步函數(shù),并返回一個函數(shù)與 calc 有同樣的參數(shù)和返回值。
下面演示我們?nèi)绾螌崿F(xiàn) timeIt 函數(shù):
const timeIt = R.curry((report, f) => (...args) => { const t0 = Date.now() const nArgs = R.init(args) const callback = R.last(args) nArgs.push((...args) => { const t1 = Date.now() callback(...args) report(t1 - t0, ...args) }) f(...nArgs) }) const timeIt1 = timeIt( (t, err, res) => console.log(`Log: ${err || res} produced after: ${t}`) ) const calc = (x, y, z, callback) => setTimeout(() => callback(null, x * y / z), 1000) calc(18, 7, 3, (err, res) => console.log(err || res)) timeIt1(calc)(18, 7, 3, (err, res) => console.log(err || res))
這個 timeIt
實現(xiàn)接受兩個參數(shù):
report: 一個函數(shù)用來生成剖析結(jié)果
f: 我們想要做剖析的異步函數(shù)
timeIt1
是一個方便實用的功能函數(shù),它只是用 console.log
記錄時間測量結(jié)果。我們通過給更通用的 timeIt
函數(shù)傳入 report
參數(shù)來定義它。
我們實現(xiàn)了目標(biāo),現(xiàn)在我們可以僅僅將異步函數(shù)包裝在 timeIt1
中就可以對它計時了:
timeIt1(calc)(18, 7, 3, (err, res) => console.log(err || res))
通用的 timeIt
函數(shù)接收一個 report
回調(diào)函數(shù)和一個異步函數(shù)并返回一個新的異步函數(shù),這個異步函數(shù)與原函數(shù)有同樣的參數(shù)和返回值。我們可以這么使用:
timeIt( (time, ...result) => // report callback: log the time , asyncFunc )( parameters…, (...result) => // result of the async function )
現(xiàn)在讓我們深入 timeIt
的實現(xiàn)。我們可以簡單地生成一個通用函數(shù)類似 timeIt1
,因為 timeIt
使用 R.curry
科里化了。
我不打算在這篇文章里討論科里化,但是下面這段代碼演示了科里化的主要用法:
const f = R.curry((x, y) => x + y) f(1, 10) // == 11 f(1)(10) // == 11 const plus1 = f(1) plus1(10) // == 11
另一方面,這種方式實現(xiàn)的 timeIt 有幾個問題:
(...args) => { const t1 = Date.now() callback(...args) report(t1 — t0, ...args) }
這是一個匿名函數(shù)(又名 lambda,callback),它在原函數(shù)異步執(zhí)行之后被調(diào)用。主要的問題是這個函數(shù)沒有處理異常的機制。如果 callback
拋出異常,report
就永遠不會被調(diào)用。
我們可以添加一個 try / catch
到這個 lambda
函數(shù)里,然而問題的根源是 callback
和 report
是兩個 void
函數(shù),它們沒有關(guān)聯(lián)在一起。timeIt
包含兩個延續(xù)(continuations)(report
和 callback
)。如果我們只是在 console
下記錄執(zhí)行時間或者如果我們確定不論 report
還是 callback
都不會拋出異常,那么一切正常。但是如果我們想要根據(jù)剖析結(jié)果來執(zhí)行一些行為(所謂的自動擴容)那么我們需要強化和厘清我們的程序中的延續(xù)序列。
好了,以上這篇文章的全部內(nèi)容,希望對大家的學(xué)習(xí)和工作有所幫助,如果有疑問可以留言交流。
相關(guān)文章
推薦三款日期選擇插件(My97DatePicker、jquery.datepicker、Mobiscroll)
這篇文章主要介紹了推薦三款日期選擇插件,My97DatePicker、用于PC端的jquery.datepicker,以及專注于移動端的Mobiscroll,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04基于JavaScript實現(xiàn) 網(wǎng)頁切出 網(wǎng)站title變化代碼
這篇文章主要介紹了基于JavaScript實現(xiàn) 網(wǎng)頁切出 網(wǎng)站title變化代碼的相關(guān)資料,需要的朋友可以參考下2016-04-04初學(xué)js 新節(jié)點的創(chuàng)建 刪除 的步驟
對于js 我是個初學(xué)者 甚至還不入門,我比較喜歡js做出的特效。2011-07-07Java通過WebSocket實現(xiàn)異步導(dǎo)出解決思路
這篇文章主要介紹了通過WebSocket實現(xiàn)異步導(dǎo)出,本篇文章記錄大批量數(shù)據(jù)導(dǎo)出時間過長,導(dǎo)致接口請求超時問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2024-01-01