前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 ES6 来为异步函数记录执行时间

使用 ES6 来为异步函数记录执行时间

作者头像
疯狂的技术宅
发布2019-03-28 11:13:57
1K0
发布2019-03-28 11:13:57
举报
文章被收录于专栏:京程一灯京程一灯

在这篇文章里,我会实现一个可重用的函数来处理 JavaScript 延时异步操作。

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

代码语言:javascript
复制
calc(arg, (err, res) => console.log(err || res))

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

代码语言:javascript
复制
const t0 = Date.now()
calc(arg, (err, res) => {
 const t1 = Date.now() 
 console.log(Log: time: ${t1 = t0})
 console.log(err || res)
})

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

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

代码语言:javascript
复制
timeIt(calc)(arg, (err, res) => console.log(err || res))

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

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

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

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

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

代码语言:javascript
复制
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))

我使用了神奇的 Ramda 库。你可以在 Ramda REPL 执行上面这段代码。

这个 timeIt 实现接受两个参数:

  • report: 一个函数用来生成剖析结果
  • f: 我们想要做剖析的异步函数

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

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

代码语言:javascript
复制
timeIt1(calc)(18, 7, 3, (err, res) => console.log(err || res))

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

代码语言:javascript
复制
timeIt(
   (time, ...result) => // report callback: log the time
 , asyncFunc
)(
  parameters…, 
  (...result) => // result of the async function
)

现在让我们深入 timeIt 的实现。我们可以简单地生成一个通用函数类似 timeIt1,因为 timeIt 使用 R.curry 科里化了。

我不打算在这篇文章里讨论科里化,但是下面这段代码演示了科里化的主要用法:

代码语言:javascript
复制
const f = R.curry((x, y) => x + y)
f(1, 10)  // == 11
f(1)(10)  // == 11

const plus1 = f(1)
plus1(10) // == 11

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

代码语言:javascript
复制
(...args) => {
  const t1 = Date.now()
  callback(...args)
  report(t1 — t0, ...args)
}

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

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-08-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 京程一灯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档