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 相关文章推荐
jQuery随机切换图片的小例子
Apr 18 Javascript
正则表达式中特殊符号及正则表达式的几种方法总结(replace,test,search)
Nov 26 Javascript
使用ajax+jqtransform实现动态加载select
Dec 01 Javascript
JS+CSS实现可拖拽的漂亮圆角特效弹出层完整实例
Feb 13 Javascript
jQuery实现简单的列表式导航菜单效果代码
Aug 31 Javascript
DIV+CSS+jQ实现省市联动可扩展
Jun 22 Javascript
jQuery简单实现iframe的高度根据页面内容自适应的方法
Aug 01 Javascript
微信小程序 Image API实例详解
Sep 30 Javascript
js实现数字递增特效【仿支付宝我的财富】
May 05 Javascript
underscore之function_动力节点Java学院整理
Jul 11 Javascript
vue实现整屏滚动切换
Jun 29 Javascript
js实现数字跳动到指定数字
Aug 25 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
重料打造自己的“宝马”---第三代
2021/03/02 无线电
一首老MP3,致敬WAR3经典
2021/03/08 魔兽争霸
php 团购折扣计算公式
2011/11/24 PHP
对PHP PDO的一些认识小结
2015/01/23 PHP
Laravel中的chunk组块结果集处理与注意问题
2018/08/15 PHP
Thinkphp框架使用list_to_tree 实现无限级分类列出所有节点示例
2020/04/04 PHP
javascript中的原型链深入理解
2014/02/24 Javascript
JS实现控制表格单元格垂直对齐的方法
2015/03/30 Javascript
基于jquery实现图片上传本地预览功能
2016/01/08 Javascript
第九章之路径分页标签与徽章组件
2016/04/25 Javascript
jQuery轻松实现表格的隔行变色和点击行变色的实例代码
2016/05/09 Javascript
全面解析JavaScript里的循环方法之forEach,for-in,for-of
2020/04/20 Javascript
Nodejs--post的公式详解
2017/04/29 NodeJs
Angularjs的启动过程分析
2017/07/18 Javascript
在vue中实现简单页面逆传值的方法
2017/11/27 Javascript
JS实现移动端在线签协议功能
2019/08/22 Javascript
vue中实现上传文件给后台实例详解
2019/08/22 Javascript
vue-devtools的安装和使用步骤详解
2019/10/17 Javascript
vue mvvm数据响应实现
2020/11/11 Javascript
[01:03:18]DOTA2-DPC中国联赛 正赛 RNG vs Dynasty BO3 第一场 1月29日
2021/03/11 DOTA
详解Django框架中的视图级缓存
2015/07/23 Python
Python之日期与时间处理模块(date和datetime)
2017/02/16 Python
python爬虫实现教程转换成 PDF 电子书
2017/02/19 Python
python中in在list和dict中查找效率的对比分析
2018/05/04 Python
python SQLAlchemy的Mapping与Declarative详解
2019/07/04 Python
Django models.py应用实现过程详解
2019/07/29 Python
python字典setdefault方法和get方法使用实例
2019/12/25 Python
小程序canvas中文字设置居中锚点
2019/04/16 HTML / CSS
南非最大的在线时尚商店:Zando
2019/07/21 全球购物
阿联酋手表和配饰购物网站:Rivolishop
2019/11/25 全球购物
Yahoo-PHP面试题3
2012/01/14 面试题
学前教育教师求职自荐信
2013/09/22 职场文书
护士专业推荐信
2013/11/02 职场文书
保护环境倡议书500字
2014/05/19 职场文书
Go语言安装并操作redis的go-redis库
2022/04/14 Golang
docker compose 部署 golang 的 Athens 私有代理问题
2022/04/28 Servers