回调蛋糕 —— cake下顺序执行命令

cake是CoffeeScript自带的make工具,简单易用。不过有一个问题,由于Node.js默认是异步的,因此你很难保证执行顺序。html

如何解决呢?其实用回调函数就能够。node

例如,假定咱们平时使用config.json(方便开发和测试),可是在发布NPM包的时候,但愿发布config.json.example。这就要求咱们在npm publish先后修改文件名——也就是说,要保证执行的顺序,不能出现文件名还没改好就发布的状况。若是用sh表达的话,就是:shell

mv config.json config.json.example
npm publish
mv config.json.example config.json

硬来的话,是这么写:npm

{spawn} = require 'child_process'

spawn 'mv', ['config.json', 'config.json.example']
spawn 'npm', ['publish']
spawn 'mv', ['config.json.example', 'congfig.json']

硬蛋糕可很差吃!这样是不行的,由于child_process.spawn是异步的。json

因此,咱们须要把上面的代码改为回调的形式,确保上一步退出时才调用下一步:异步

mv = spawn 'mv', ['config.json', 'config.json.example']
mv.on 'exit', ->
  npm_publish = spawn 'npm', ['publish']
  npm_publish.on 'exit', ->'
    spawn 'mv', ['config.json.example', 'congfig.json']

这样就能够了。但是每次这么写还蛮烦的。咱们把它抽象成一个函数。函数

嗯,该怎么写呢?首先,咱们肯定这个函数的输入,函数应当接受一个命令,后面是一串回调函数。所以:工具

shell_commands = (args...) ->

args...是splats,用来表示参数数目不定。测试

而后咱们须要作的就是执行这个命令,在命令执行完毕以后,运行那一串回调函数。ui

所以,咱们首先获得须要执行的命令,这很简单,pop掉最后的一串回调函数,再交给spawn执行便可。

args.pop()
[command, args...] = args
spawn command, args

注意上面的args...一样是splats,此次用于模式匹配。

而后加上运行回调函数的代码,咱们的函数基本就成形了:

shell_commands = (args...) ->
  callback = args.pop()
  [command, args..] = args
  shell_command = spawn command, args
  shell_command.on 'exit' ->
    callback()

有个问题,回调到最后,会是单纯的命令,没有回调函数了。所以咱们加一个判断,若是args的最后一项不是函数的话,那就不pop了,直接执行命令就好了:

shell_commands = (args...) ->
  if typeof(args[a.length - 1]) is 'function'
    callback = args.pop()
  else
    callback = ->
  [command, args..] = args
  shell_command = spawn command, args
  shell_command.on 'exit' ->
    callback()

抽象出了这个函数之后,咱们原先的代码就能够这么写了:

shell_commands 'mv', 'config.json', 'config.json.example', ->
  shell_commands 'npm', 'publish', ->
    shell_commands 'mv', 'config.json.example', 'config.json'

是否是清爽多了?

没有处理的问题是,万一在重命名的过程当中出问题了,那么咱们不该该发布NPM包。因此,咱们加一下判断,若是进程的exit code不为0(这就意味着有错误),那么继续执行。

shell_commands = (args...) ->
  if typeof(args[a.length - 1]) is 'function'
    callback = args.pop()
  else
    callback = ->
  [command, args..] = args
  shell_command = spawn command, args
  shell_command.on 'exit', (code) ->
    if code isnt 0
      callback(code)
    else
      console.log("Exited with status: " + code)

相应地,调用的时候也须要传入code

shell_commands 'mv', 'config.json', 'config.json.example', (code) ->
  shell_commands 'npm', 'publish', (code) ->
    shell_commands 'mv', 'config.json.example', 'config.json'

一般你们喜欢verbose的输出,那么咱们就在终端显示一下执行的命令:

console.log(command, " ", args.join(" "))

最终的CakeFile相似这样:

{spawn} = require 'child_process'

shell_commands = (args...) ->
  if typeof(args[a.length - 1]) is 'function'
    callback = args.pop()
  else
    callback = ->
  [command, args..] = args
  console.log(command, " ", args.join(" "))
  shell_command = spawn command, args
  shell_command.on 'exit', (code) ->
    if code isnt 0
      callback(code)
    else
      console.log("Exited with status: " + code)

task "publish", "publish NPM", ->
  shell_commands 'mv', 'config.json', 'config.json.example', (code) ->
    shell_commands 'npm', 'publish', (code) ->
      shell_commands 'mv', 'config.json.example', 'config.json'

task "test", "run unit tests", ->
  shell_commands './node_module/.bin/mocha'
相关文章
相关标签/搜索