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'