ES6记录异步函数的执行时间详解


Posted in Javascript onAugust 31, 2016

calc

calc 是一个我们想要做剖析(性能分析)的异步函数。按照惯例,它的最后一个参数是一个callback。我们像这样使用 calc

calc(arg, (err, res) => console.log(err || res))

或许,最简单的对 calc 这样的函数来剖析性能的方法是,增加一个计时逻辑到我们需要分析的地方:

const t0 = Date.now()
calc(arg, (err, res) => {
 const t1 = Date.now() 
 console.log(`Log: time: ${t1 = t0}`)
 console.log(err || res)
})

但是,这不是一个可复用的解决方案。每一次我们想要对一个函数计时,我们得引入一个 t0 在外层作用域并且改变 callback 来测量和记录时间。

对我来说理想的方式是能够仅仅通过包装一个异步函数就能够对它进行计时:

timeIt(calc)(arg, (err, res) => console.log(err || res))

timeIt 需要能够很好地对每一个异步函数完成剖析和记录执行时间。

注意到 timeIt(calc) 有与原始的 calc 函数同样的函数签名,即它们接受同样的参数和返回同样的值,它只是增加了一个特性到 cale 上(能够被记录时间的特性)。

calc 和 timeIt(calc) 在任意时刻可以相互替代。

timeIt 本身是一个高阶函数,因为它接受一个函数并返回一个函数。在我们的例子里,它接受 calc 异步函数,并返回一个函数与 calc 有同样的参数和返回值。

下面演示我们如何实现 timeIt 函数:

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 实现接受两个参数:

      report: 一个函数用来生成剖析结果

      f: 我们想要做剖析的异步函数

timeIt1 是一个方便实用的功能函数,它只是用 console.log 记录时间测量结果。我们通过给更通用的 timeIt 函数传入 report 参数来定义它。

我们实现了目标,现在我们可以仅仅将异步函数包装在 timeIt1 中就可以对它计时了:

timeIt1(calc)(18, 7, 3, (err, res) => console.log(err || res))

通用的 timeIt 函数接收一个 report 回调函数和一个异步函数并返回一个新的异步函数,这个异步函数与原函数有同样的参数和返回值。我们可以这么使用:

timeIt(
 (time, ...result) => // report callback: log the time
 , asyncFunc
)(
 parameters…, 
 (...result) => // result of the async function
)

现在让我们深入 timeIt 的实现。我们可以简单地生成一个通用函数类似 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

另一方面,这种方式实现的 timeIt 有几个问题:

(...args) => {
 const t1 = Date.now()
 callback(...args)
 report(t1 — t0, ...args)
}

这是一个匿名函数(又名 lambda,callback),它在原函数异步执行之后被调用。主要的问题是这个函数没有处理异常的机制。如果 callback 抛出异常,report 就永远不会被调用。

我们可以添加一个 try / catch 到这个 lambda 函数里,然而问题的根源是 callback report 是两个 void 函数,它们没有关联在一起。timeIt 包含两个延续(continuations)(report callback)。如果我们只是在 console 下记录执行时间或者如果我们确定不论 report 还是 callback 都不会抛出异常,那么一切正常。但是如果我们想要根据剖析结果来执行一些行为(所谓的自动扩容)那么我们需要强化和厘清我们的程序中的延续序列。

好了,以上这篇文章的全部内容,希望对大家的学习和工作有所帮助,如果有疑问可以留言交流。

Javascript 相关文章推荐
BAT及各大互联网公司2014前端笔试面试题--JavaScript篇
Oct 29 Javascript
关于获取DIV内部内容报错的原因分析及解决办法
Jan 29 Javascript
Javascript获取统一管理的提示语(message)
Feb 03 Javascript
JS实现图片剪裁并预览效果
Aug 12 Javascript
Node.js复制文件的方法示例
Dec 29 Javascript
微信小程序 两种滑动方式(横向滑动,竖向滑动)详细及实例代码
Jan 13 Javascript
Javascript中字符串和数字的操作方法整理
Jan 22 Javascript
canvas绘图不清晰的解决方案
Feb 28 Javascript
JS简单封装的图片无缝滚动效果示例【测试可用】
Mar 22 Javascript
利用JS判断客户端类型你应该知道的四种方法
Dec 22 Javascript
JavaScript使用indexOf()实现数组去重的方法分析
Sep 04 Javascript
jquery获取input输入框中的值
Nov 13 jQuery
基于angularjs实现图片放大镜效果
Aug 31 #Javascript
用AngularJS的指令实现tabs切换效果
Aug 31 #Javascript
简洁实用的BootStrap jQuery手风琴插件
Aug 31 #Javascript
AngularJS实现一次监听多个值发生的变化
Aug 31 #Javascript
利用Angularjs和bootstrap实现购物车功能
Aug 31 #Javascript
JavaScript String(字符串)对象的简单实例(推荐)
Aug 31 #Javascript
基于JavaScript实现鼠标向下滑动加载div的代码
Aug 31 #Javascript
You might like
深入PHP中慎用双等于(==)的详解
2013/06/06 PHP
PHP中读取照片exif信息的方法
2014/08/20 PHP
PHP中创建和验证哈希的简单方法实探
2015/07/06 PHP
CI操作cookie的方法分析(基于helper类库)
2016/03/28 PHP
修改jQuery.Autocomplete插件 支持中文输入法 避免TAB、ENTER键失效、导致表单提交
2009/10/11 Javascript
JavaScript之编码规范 推荐
2012/05/23 Javascript
javascript的创建多行字符串的7种方法
2014/04/29 Javascript
js添加千分位的实现代码(超简单)
2016/08/01 Javascript
实例解析jQuery中如何取消后续执行内容
2016/12/01 Javascript
手机端js和html5刮刮卡效果
2020/09/29 Javascript
浅谈js中function的参数默认值
2017/02/20 Javascript
js实现一键复制功能
2017/03/16 Javascript
JavaScript面向对象的程序设计(犯迷糊的小羊)
2018/05/27 Javascript
解决Idea、WebStorm下使用Vue cli脚手架项目无法使用Webpack别名的问题
2019/10/11 Javascript
解决vue动态下拉菜单 有数据未反应的问题
2020/08/06 Javascript
[03:55]显微镜下的DOTA2特别篇——430灰烬之灵神级操作
2014/06/24 DOTA
[00:30]明星选手化身超级英雄!2018DOTA2亚洲邀请赛全明星赛来临!
2018/04/06 DOTA
python赋值操作方法分享
2013/03/23 Python
python修改注册表终止360进程实例
2014/10/13 Python
Python下rrdtool模块的基本使用方法
2015/11/13 Python
Python使用matplotlib绘制多个图形单独显示的方法示例
2018/03/14 Python
详解Django CAS 解决方案
2019/10/30 Python
Python实现报警信息实时发送至邮箱功能(实例代码)
2019/11/11 Python
如何基于Python获取图片的物理尺寸
2019/11/25 Python
Spring Boot中使用IntelliJ IDEA插件EasyCode一键生成代码详细方法
2020/03/20 Python
python中matplotlib实现随鼠标滑动自动标注代码
2020/04/23 Python
Python爬虫简单运用爬取代理IP的实现
2020/12/01 Python
详解css3 mask遮罩实现一些特效
2018/10/24 HTML / CSS
环境工程大学生个人的自我评价
2013/10/08 职场文书
优秀毕业生自我鉴定
2014/02/11 职场文书
小学优秀班主任事迹材料
2014/05/17 职场文书
院党委组织查摆问题对照检查材料思想汇报2014
2014/10/08 职场文书
趵突泉导游词
2015/02/03 职场文书
教师远程研修感悟
2015/11/18 职场文书
服务行业标语口号
2015/12/26 职场文书
SQL实现LeetCode(177.第N高薪水)
2021/08/04 MySQL