Skip to content
winterland edited this page Oct 30, 2015 · 15 revisions

Action.signal

Sometimes human's input are the async action we want to deal with, for example when you want to validate a large form, or write a interactive game, take a few examples on jsbin:

Action's solution to these problems are signal and pump:

var Action.signal = new Action(function(cb){
    return cb;
})

That's all the code of signal! it's just a special Action which never call the cb, instead it return cb directly. Let see it's in action:

var logSignal = Action.signal
.guard(function(err){
    console.log(err.message);
    return false;
})
.next(function(data){
    console.log(data);
    return true;
});

var logPump = logSignal.go()

Make sure you read Return value of go, when you fired a signal with go, you get a pump back, which is the callback chain you built with next and guard. It's easy to see logPump above is equivalent to:

function(data){
    if (data instance of Error){
        console.log(err.message);
        return false;
    } else {
        console.log(data);
        return true;
    }
}

Note we skip the instanceof Action stuff, Now let pump a value into it:

logPump('hello world')
// hello world

logPump(new Error('damn'))
// damn

If you want to sent a value to backend to do validation, just return a validation Action inside next:

var logSignal = Action.signal
.next(function(data){
    return someAsyncValidation(data);
    // this should be a Action
}
.next(function(data){
    console.log('The validation result is:' + data);
});

Since a signal is just an Action, any Action can be easily composed into a signal, and you can connect one signal to another signal's pump like this:

composedSignal = signalOne.next(signalTwo.go())

Another intereting thing about signal is, it's an Action never fired unless we pump something into it, so now controll comes back to us, it can be fired multiple times, so we have a special combinator for signals:

var pumps = Action.fuseSignal([ logSignal, logSignal ])
.next(function(data){
    console.log(data[0], data[1]);    
})
.go()

setTimeout(pumps[0], 100, 'foo'); 
setTimeout(pumps[1], 1000, 'bar');

// after 100ms, 'foo' will be logged.
// after 1000ms, 'bar' will be logged.
// after 'bar' was logged, true, true will also be logged.

setTimeout(pumps[1], 2000, 'bar');
// true, true
setTimeout(pumps[1], 3000, new Error 'testError');
// true, false

This combinator combine an Array of signals into one, and you got a pumps array using go, now only if you pumped all the pumps, the composed signal will be pumped. It's different to Action.all, after all pumps have been pumped, fused signal will be continue pumped everytime you pump a value into it. If you enable second parameter, Action.fuseSignal will behave like a validator, which any error will be report and only after all results are not errors, action will continue. check the form validation demo, try remove/add the second param of Action.fuseSignal and find the difference.

Clone this wiki locally