Wrappers
There are many functions out there that return results via callbacks. In order to use them in your nsynjs-executed code you just need to wrap them, so they would be able to post results of callback back to nsynjs, and resume execution of nsynjs engine.
Q: Wait, why would I need wrappers, if I already have promisified functions?
A: Wrappers provide some extra functionality:
- ability to cleanup all pending callbacks in case if caller pseudo-thread is stopped externally via
ctx.stop()
- ability to handle functions that trigger unknown number of callbacks (see readline example that uses nodeReadline wrapper),
- ability to return error instead of triggering an exception.
All nsynjs-aware wrappers should generally do following:
- Accept reference to a current pseudo-thread as a parameter (e.g. ctx).
- Call wrapped function
- Return an object, and assign results of the callback to some properties of that object.
- Call ctx.resume() in th callback of wrapped fucntion, so caller pseudo-thread will continue execution.
- Set destructor function, that will be called in order to cancel long-running function.
Note: if wrapper may return without calling function with callback, nsynjs needs to be notified about this by calling ctx.setDoNotWait(true) right before return statement.
All nsynjs-aware wrapper should have 'nsynjsHasCallback' property set to true.
Here is an example of simple wrapper to setTimeout:
var wait = function (ctx, ms) {
setTimeout(function () {
ctx.resume(); // <<-- resume execution of nsynjs pseudo-thread, referred by ctx
}, ms);
};
wait.nsynjsHasCallback = true; // <<-- indicates that nsynjs should stop and wait when calling this function
Example of wrapper to setTimeout, that will be gracefully stopped in case if pseudo-thread is stopped:
var wait = function (ctx, ms) {
var timeoutId = setTimeout(function () {
console.log('firing timeout');
ctx.resume();
}, ms);
ctx.setDestructor(function () { // <<-- this function will be called in case if pseudo-thread is requested to stop
console.log('clear timeout');
clearTimeout(timeoutId);
});
};
wait.nsynjsHasCallback = true;
Note: if wrapper may return without calling function with callback, nsynjs needs to be notified about this by calling ctx.setDoNotWait(true) right before return statement:
var wait = function (ctx, ms, condition) {
if(condition) {
ctx.setDoNotWait(true); // <-- function with callback is not going to be called by wrapper,
// therefore caller may continue execution
return;
}
var timeoutId = setTimeout(function () {
console.log('firing timeout');
ctx.resume();
}, ms);
ctx.setDestructor(function () {
console.log('clear timeout');
clearTimeout(timeoutId);
});
};
wait.nsynjsHasCallback = true;
See wrappers/nodeReadline.js for example of wrapper with conditional callbacks.
Example of wrapper to jQuery's getJSON, that can return data or throw an exception back to nsynjs-executed code:
var ajaxGetJson = function (ctx,url) {
var res = {}; // <<-- results will be posted back to nsynjs via method to this object
var ex; // <<-- possible exception
$.getJSON(url, function (data) {
res.data = data; // <<-- capture data from callback, or
})
.fail(function(e) {
ex = e; // <<-- capture exception
})
.always(function() {
ctx.resume(ex); // <<-- resume pseudo-thread
});
return res;
};
ajaxGetJson.nsynjsHasCallback = true; // <<-- indicates that nsynjs should stop and wait on evaluating this function
Wrappers for some common functions could be found in /wrappers folder. See JSDOCs.