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 EasyUI 开源插件套装 完全替代ExtJS
Mar 24 Javascript
Dom操作之兼容技巧分享
Sep 20 Javascript
Ext JS添加子组件的误区探讨
Jun 28 Javascript
javascript内存管理详细解析
Nov 11 Javascript
JavaScript中reduce()方法的使用详解
Jun 09 Javascript
JS+HTML5实现的前端购物车功能插件实例【附demo源码下载】
Oct 17 Javascript
将JSON字符串转换成Map对象的方法
Nov 30 Javascript
jQuery插件JWPlayer视频播放器用法实例分析
Jan 11 Javascript
Bootstrap modal 多弹窗之叠加关闭阴影遮罩问题的解决方法
Feb 27 Javascript
浅谈react.js中实现tab吸顶效果的问题
Sep 06 Javascript
vue.js实现简单购物车功能
May 30 Javascript
全面解析js中的原型,原型对象,原型链
Jan 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
全国FM电台频率大全 - 11 浙江省
2020/03/11 无线电
利用js调用后台php进行数据处理原码
2006/10/09 PHP
支持中文的php加密解密类代码
2011/11/27 PHP
php求数组全排列,元素所有组合的方法
2016/05/05 PHP
JS完成代码前最好对其做5件事
2013/04/07 Javascript
使用jQuery重置(reset)表单的方法
2014/05/05 Javascript
js拖拽功能实现代码解析
2016/11/28 Javascript
JS实现Ajax的方法分析
2016/12/20 Javascript
Vue按需加载的具体实现
2017/12/02 Javascript
JS同步、异步、延迟加载的方法
2018/05/05 Javascript
详解vue-router 初始化时做了什么
2018/06/11 Javascript
JavaScript求一个数组中重复出现次数最多的元素及其下标位置示例
2018/07/23 Javascript
vue mounted 调用两次的完美解决办法
2018/10/29 Javascript
layer关闭当前窗口页面以及确认取消按钮的方法
2019/09/09 Javascript
python爬虫框架scrapy实战之爬取京东商城进阶篇
2017/04/24 Python
Python使用内置json模块解析json格式数据的方法
2017/07/20 Python
Python Flask前后端Ajax交互的方法示例
2018/07/31 Python
python中的for循环
2018/09/28 Python
详解从Django Rest Framework响应中删除空字段
2019/01/11 Python
使用python自动追踪你的快递(物流推送邮箱)
2020/03/17 Python
浅谈Python里面None True False之间的区别
2020/07/09 Python
浅谈盘点5种基于Python生成的个性化语音方法
2021/02/05 Python
印尼在线精品店:Berrybenka.com
2016/10/22 全球购物
创造美妙香氛体验:Aera扩散器和香水
2018/11/25 全球购物
构造方法和其他方法的区别?怎么调用父类的构造方法
2013/09/22 面试题
师德个人剖析材料
2014/02/02 职场文书
中学校庆方案
2014/03/17 职场文书
《庐山的云雾》教学反思
2014/04/22 职场文书
银行柜员求职自荐书
2014/06/18 职场文书
大学生安全责任书
2014/07/25 职场文书
2015年社区工作总结
2015/04/08 职场文书
幼儿园托班教育随笔
2015/08/14 职场文书
人事行政部各岗位职责说明书!
2019/07/15 职场文书
详解Flask开发技巧之异常处理
2021/06/15 Python
python基于turtle绘制几何图形
2021/06/15 Python
【海涛教你打dota】体验一超神发条:咱是抢盾专业户
2022/04/01 DOTA