Skip to content

Instantly share code, notes, and snippets.

@PeterHancock
Created February 28, 2014 13:04
Show Gist options
  • Save PeterHancock/9270706 to your computer and use it in GitHub Desktop.
Save PeterHancock/9270706 to your computer and use it in GitHub Desktop.
async shotgun
//cmd.js
exports.invoke = function(shell, options) {
shell.emit('async', function(resume) {
someAsyncFn(function(result){
shell.log(result);
resume();
});
//app.js
var rl = require('readline'),
shell = ...
var ready = true;
//..constructors ..
rl.on('line', function(userInput) {
shell.execute(userInput);
if (ready) {
rl.prompt();
}
});
shell.on('async', function(fn) {
ready = false;
fn(function(){
ready = true;
rl.prompt();
})
});
@catdadcode
Copy link

I think I need a little more information on this one. Can you tell me what you need to have happen with regard to asynchronous calls? The nature of shotgun is that the moment you use a helper such as shell.log an event is immediately emitted out the other end. This means that you should be able to use shell helpers in async callbacks just fine.

exports.invoke  = function (shell, options) {
    shell.log("Starting async task...");
    fs.readFile(pathToFile, function (err, fileData) {
        if (err) return shell.error(err);
        shell.log("File retrieved.");
        shell.log(fileData.toString());
    });
};

Can you tell me what problem the async code above causes? You're the first person to use shotgun so extensively since its a fairly complex and new library so any more information you can offer would be really helpful. The only thing that I can think of that would happen prematurely in the above scenario is that the shell would emit the "done" event before the async was actually done. That alone is reason enough to provide some kind of callback; I just want to know if there are additional side effects that you are seeing so I can cover all the bases.

@catdadcode
Copy link

Nevermind. I think I'm understanding. The readline prompt resumes before the async is finished. I think I have the solution for this. I'm going to scan the invoke function signature and count the number of arguments the function is expecting. If it asks for two then I will assume they are shell and options and the function will behave synchronously. If it asks for three then the third argument will be a callback that your command can call when it's all finished.

All this callback will do is fire the "done" event from the shell. This means that you'll be able to handle the done event and do your rl.prompt() from there.

Thank you for pointing out this flaw :)

Here's what my intended solution will look like when I'm done:

exports.invoke = function (shell, options, done) {
    shell.log("Starting async task...");
    fs.readFile(pathToFile, function (err, fileData) {
        if (err) return shell.error(err);
        shell.log("File retrieved.");
        shell.log(fileData.toString());
        done();
    });
};

I will also make done accept an err argument which will cause the shell to emit an error event before the done event.

@PeterHancock
Copy link
Author

That sounds great! I assume the done event will only be fired at the end of execute if invoke is synchronous

@catdadcode
Copy link

That's correct. catdadcode/shotgun#23

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment