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 相关文章推荐
为JS扩展Array.prototype.indexOf引发的问题及解决办法
Jan 21 Javascript
JavaScript数组随机排列实现随机洗牌功能
Mar 19 Javascript
JavaScript获得url所有参数键值表的方法
Mar 21 Javascript
JavaScript使用pop方法移除数组最后一个元素用法实例
Apr 06 Javascript
一种基于浏览器的自动小票机打印实现方案(js版)
Jul 26 Javascript
H5上传本地图片并预览功能
May 08 Javascript
Angular实现类似博客评论的递归显示及获取回复评论的数据
Nov 06 Javascript
JavaScript通过mouseover()实现图片变大效果的示例
Dec 20 Javascript
ES6下子组件调用父组件的方法(推荐)
Feb 23 Javascript
使用wxapp-img-loader自定义组件实现微信小程序图片预加载功能
Oct 18 Javascript
解决vue项目F5刷新mounted里的函数不执行问题
Nov 05 Javascript
javascript开发实现贪吃蛇游戏
Jul 31 Javascript
基于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
Apache, PHP在Windows 9x/NT下的安装与配置 (二)
2006/10/09 PHP
Windows下XDebug 手工配置与使用说明
2010/07/11 PHP
PHP 验证码不显示只有一个小红叉的解决方法
2013/09/30 PHP
教你如何快捷的使用cmd访问mysql小技巧
2014/05/26 PHP
PHP使用pear实现mail发送功能 windows环境下配置pear
2016/04/15 PHP
详解php用curl调用接口方法,get和post两种方式
2017/01/13 PHP
PHP实现的简单异常处理类示例
2017/05/04 PHP
JS 修改URL参数(实现代码)
2013/07/08 Javascript
JS画5角星方法介绍
2013/09/17 Javascript
JavaScript对Cookie进行读写操作实例
2015/07/25 Javascript
在AngularJS框架中处理数据建模的方式解析
2016/03/05 Javascript
jQuery实现HTML表格单元格的合并功能
2016/04/06 Javascript
bootstrap与Jquery UI 按钮样式冲突的解决办法
2016/09/23 Javascript
如何学JavaScript?前辈的经验之谈
2016/12/28 Javascript
webpack配置的最佳实践分享
2017/04/21 Javascript
移动端Ionic App 资讯上下循环滚动的实现代码(跑马灯效果)
2017/08/29 Javascript
jQuery实现购物车的总价计算和总价传值功能
2018/11/28 jQuery
JS 数组基本用法入门示例解析
2020/01/16 Javascript
[03:26]《DAC最前线》之EG经理自述DOTA2经历
2015/02/02 DOTA
[58:15]2018DOTA2亚洲邀请赛 4.1 小组赛 A组 NB vs Liquid
2018/04/02 DOTA
[01:05:07]DOTA2-DPC中国联赛 正赛 DLG vs Dragon BO3 第一场2月1日
2021/03/11 DOTA
django2.0扩展用户字段示例
2019/02/13 Python
解决Python3 抓取微信账单信息问题
2019/07/19 Python
基于Python数据结构之递归与回溯搜索
2020/02/26 Python
Python能做什么
2020/06/02 Python
python 获取计算机的网卡信息
2021/02/18 Python
将世界上最美丽的摄影作品转化为艺术作品:Photos.com
2017/11/28 全球购物
JD Sports瑞典:英国领先的运动时尚商店
2018/01/28 全球购物
State Cashmere官网:半零售价可持续蒙古羊绒
2020/02/26 全球购物
医院门卫岗位职责
2013/12/30 职场文书
室内拓展活动方案
2014/02/13 职场文书
高级销售求职信
2014/02/21 职场文书
四风个人对照检查材料思想汇报(办公室通用版)
2014/10/07 职场文书
个人批评与自我批评
2014/10/15 职场文书
redis 限制内存使用大小的实现
2021/05/08 Redis
python中urllib包的网络请求教程
2022/04/19 Python