Synchronous Exec in Node.js

By  on  

Everyone loves NodeJS in part because it embraces a non-blocking philosophy;  interactions are asynchronous and thus, theoretically, allow for faster all-around processing.  When creating a simple script for my upcoming redesign, I found the asynchronocity ... annoying.  I was quickly sinking into callback hell with the server executions I was running through Node.js.  Luckily I found a great package called exec-sync which allows synchronous execution of shell commands so that I don't find myself many callbacks deep.

Installing exec-sync

The exec-sync package is available via npm:

npm install exec-sync

Don't you love package management?!

Using execSync

exec-synce works like any other Node.js component;  require it and it's ready to use:

// Require the dependency
var execSync = require("exec-sync");

// The traditional way
var exec = require('child_process').exec;
exec('ORIGINAL COMMAND', function(err, stdout, stderr) {
	// React to callback

	exec('SOME OTHER COMMAND', function(err2, stdout2, stderr2) {
		// More reacting

		// ... more nesting that isn't desired
	});
});

// The execSync way

var execSync = require("exec-sync");

// Create the JavaScript dir, uglify contents
execSync("cp -R "  + sourceDir + "js " + jsDir);
doCompress && walkAndDo("js", jsDir, function(fileName, stat) {
	execSync("./node_modules/uglify-js/bin/uglifyjs -nc --reserved-names 'require,define,curl,z' --unsafe --lift-vars --overwrite " + fileName);
});

// Copy the "crons" and "cache" directories over
execSync("cp -R " + sourceDir + "crons " + dirPath + "crons");
execSync("cp -R " + sourceDir + "cache " + dirPath + "cache");

// Delete all bullshit ".DS_Store" files
execSync("find . -type f -name .DS_Store -exec rm -rf {} +");

// And when all is done, copy to another place
execSync("mv " + dirPath + " ../../../wp-content/themes/jack"); 

Note that instead of passing a callback to the execSync method, I can simply assume things are running from the top down, allowing me to avoid a bunch of nested callbacks that would only muddy up my code.  Of course some processes could be asynchronous but exec-sync allows me to keep a top-down thought process. Node.js does have utilities for a few of these functions, but running shell keeps the code shorter.

The code above was just a tiny snippet of my build file. Without exec-sync, I would either need to nest and nest and nest callbacks, or get a Deferred implementation to handle all the callbacks. Using a Deferred implementation would allow for more speed for the build process, but when the process takes only a few seconds, the tradeoff for top-down execution is worth it.

Recent Features

Incredible Demos

Discussion

  1. Brusselsblogger

    A few days ago I also tried to find a solution to the sometimes annoying asynchronous way of how code is executed in node.js.

    I then discovered that there are a multitude of solutions to deal with flow control in node.js (one being exec-sync).

    After some research I decided to go with https://github.com/kriskowal/q/ as it is one of the most popular and complete module. I like the concept of “promises” and it turns out that jQuery has a similar technique with it deferred object. For me, promises were difficult to understand, but this article helped a lot: http://erickrdch.com/2012/06/how-to-wait-for-2-asynchronous-responses-on-nodejs-commonjs-promises.html

  2. Artem

    Sorry ^^. Here is the link to formated code:

    http://pastebin.com/LkyKZi80

  3. David

    Been hearing a lot about NodeJS recently. Seems like something worth picking up. I’m a server-end developer, so my experiences with front-end developing are fleeting. =)

    Also worth note is… well… a note. On jQuery’s AJAX page: http://api.jquery.com/jQuery.ajax/
    “As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred) is deprecated; you must use the complete/success/error callbacks.”

    Seems like synchronous ajax calls are out the door for jQuery at the moment.

  4. You don’t want to do this; it does separate concerns.

  5. I think there’s some confusion here. I know how to hand asynchronous actions and I’m well aware of Deferreds in Dojo and jQuery.

    My point with this post is that there are times when synchronous, top-down execution is preferred.

  6. Billy Moon

    after installing (and calling) execSync, I get warning in my code output:

    “Utf8String” type is deprecated, use “CString” instead

    Version installed…

    exec-sync@0.1.4 ../../../../../../node_modules/exec-sync
    └── ffi@1.0.1 (ref-struct@0.0.3, debug@0.6.0, bindings@1.0.0, ref@0.1.2)

  7. This saves the stdout to a tmp/file then reads it back correct? I guess as long as this is used for build files this should be good (according to the stackoverflow people)

  8. naikrovek

    > npm install exec-sync

    if only it were that easy. I just tried it on three machines, all with python and visual studio, and it failed on each one.

    Maintainers of this and execSync need better software, or better software documentation.

  9. Mark

    I too have failed to install. It seems even with python and .net that this npm still fails. First time I’ve had such a hard time installing too.

  10. Coder

    The exec-sync is and has been working fine. Grow a pair and unwad your panties ;)

  11. You can have your cake and eat it, too — with coroutines. Check out generators, available since Node 0.11.

  12. Another approach to this is run a sequence of commands over a spawned shell that stays alive across many commands all executed sequentially (using streams to interact with it) such as https://github.com/bitsofinfo/stateful-process-command-proxy

  13. Antiquated and no longer valid advice. See this question on StackOverflow for an uptodate solution that uses the standard library.. http://stackoverflow.com/q/31819950/124486

  14. Update from the future 2017, you can use exec in a synchronous way now.
    https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options

  15. NSA
    npm install exec-sync
    ...
    npm ERR! 53 warnings and 6 errors generated.
    npm ERR! make: *** [Release/obj.target/binding/src/binding.o] Error 1
    npm ERR! gyp ERR! build error 
    npm ERR! gyp ERR! stack Error: make failed with exit code: 2
    ...
    

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!