Starting from version 0.4.0 safejs implements almost all functions of Async and can transparently replace it. For asyncjs functions library adds its error trap capabilities and also contain some optimizations and speed improovements as well as some new functions. We plan to be less conservative for all necessary changes and welcome any contributions.
Initially modules was designed to provide set of handy function to deal with thrown errors, callbacks and nodejs alike error passing (first function argument).
The goal is to make code more stable, readable and avoid some routine calls. Idea is inspired in Step library which catches thrown errors and convert them into callback function calls.
Async library which appears more handy missing this. With safe library it is easy to plug-in this when required.
Function are kind of chainable, so instead of safe.trap(safe.sure(function () {} ))
it is possible to use safe.trap_sure(function() {})
Note: In all samples below async keyword can be replaced to safe
async.series([
function (callback) {
// .. do something that can throw error
// BAD: some dirty code can throw exception here
// which breaks nodejs server
async.forEach(array, function (e, callback2) {
some.getSome(e.id, function (err, some) {
// BAD: err is not checked
// .. process some
// BAD: boring code
callback2();
})
},callback)
}]
async.series([
function (callback) {
try {
// .. do something that can throw error
// .. note ANY CODE CAN DO in some conditions
async.forEach(array, function (e, callback2) {
some.getSome(e.id, function (err, some) {
try {
if (err) return callback2(err);
// .. process some
callback2();
} catch (err) {
callback2(err);
}
})
},callback)
} catch (err) {
callback(err);
}
}]
async.series([
safe.trap(function (callback) {
async.forEach(array, function (e, callback2) {
some.getSome(e.id, safe.trap_sure_results(callback2, function (some) {
// .. process some
});
},callback)
}
We also found that in some simple cases safe functions allows to write code that does things similar to async.series or async.waterfall in bit more efficient way (at least as we think). It became possible because JavaScript closures are in effect and you not need to think how to pass variables from one function to another. In theory async.waterfall should help for such case but it has two caveats. First is when one of funtions that you used change number of paramaters returned thru callback. Code became broken and error is hard to find. Second one is that if result of one step is required later then next step you have to pass it thru intermidiate steps which makes waterfall behavior fall down to async.series. Check this on examples
var users, user, clients;
async.series([
function (callback) {
mongo.collection("users",function (err,val) {
if (err) return callback(err);
users = val;
callback();
}
},
function (callback) {
users.findOne({login:"john"},fuction (err, val) {
if (err) return callback(err);
session.user = user;
callback();
}
},
function (callback) {
mongo.collection("clients",function (err,val) {
if (err) return callback(err);
clients = val;
})
},
function (callback) {
clients.insert({uid:user.id, date: new Date()},callback)
}
], function (err) {
if (err) callback(new Error("Log-in or password is incorrect")
else callback();
}))
async.waterfall([
function (callback) {
mongo.collection("users",callback)
},
function (users,callback) {
users.findOne({login:"john"},callback)
},
function (user,callback) {
mongo.collection("clients",function (err, clients) {
if (err) return callback(err);
callback(null,user,clients)
}
}
function (user,clients,callback) {
sesson.user = user;
clients.insert({uid:user.id, date: new Date()},callback)
}
], function (err) {
if (err) callback(new Error("Log-in or password is incorrect")
else callback();
}))
safe.run(function(callback) {
mongo.collection("users",safe.sure(callback, function (users) {
users.findOne({login:"john"},safe.sure(callback, fuction (user) {
session.user = user;
mongo.collection("clients",safe.sure(callback, function (clients) {
clients.insert({uid:user.id, date: new Date()},callback)
}))
}))
}))
}, function (err) {
if (err) callback(new Error("Log-in or password is incorrect")
else callback();
}))
Transform synchronious function call result to callback
callback is optional, when omited (function get one parameter) it assumes callback as last parameter of wrapped function
@param {Function} callback or wrapped function
@param {Function} fn wrapped function
Strip (hide) first parameter from wrapped function and ensure that
controll is passed to it when no error happpens. I.e. it does do
routine error check if (err) return callback(err)
If passed non function it just returned thru callback
callback is optional, when omited (function get one parameter) it assumes callback as last parameter of wrapped function
@param {Function} callback or wrapped function
@param {Function} wrapped function or value
Wrap function call into try catch, pass thrown error to callback
callback is optional, when omited (function get one parameter) it assumes callback as last parameter of wrapped function
@param {Function} callback or wrapped function
@param {Function} fn wrapped function
Run function with provided callback. Just help to have better readability. Usefull when you need to handle local callback results, even if there are errors.
@param {Function} wrapped function
@param {Function} callback
Yields execution of function giving chance to other stuff run
@param {Function} callback
Empty function, does nothing. Sometime useful.
Run provided callback in next tick of event loop. This is what for process.nextTick was usually used. However for very strange reason starting from node v10 it fails when called recursively.
To be honest its failry stupid because now process.nextTick became useless because will it work or not depends on callee. It was honest just to say to stop use process.nextTick, there is no any purpose for it any longer. Function safe.run will use process.nextTick or its new successor setImmediate in newer versions of node.
Anyway process.nextTick usually was used to break recursion or to maintain async behavior when function return something right away without calling trully async function (IO mostly). It was required to write something like:
if (cached)
return process.nextTick(function () {callback(null,cached})
with back:
if (cached)
return safe.back(callback,null,cached)
@param {Function} callback
@param argument1
@param argumentN
Wraps function with several parameters into function that accept array of paramters. Useful to process async.series or async.parallel result calls.
@param {Function} - normal function
Normal async code:
], function (err, res) {
if (err) return callback(err)
var me = res[0];
var users = res[1];
Using safe:
],safe.sure_spread(callback, function (me, users) {
Complimentary helper function to "async" libray similar to async.apply. The difference is that it bind function call to some specific object context.
@param {Object} - context object
@param {String} - function name
@param ... - function arguments
Normal async code:
async.series([
function (cb) {
users.findOne({gender:"male"},cb)
}
Using safe:
async.series([
safe.async(users,"findOne",{gender:"male"})
Similar to trap but has reverse order of parameters. Both paramters are required. Callback will be appended to wrapped function as last parameter. Useful to simulate try catch behavior for functions that didn't receive callback but still need error handling
@param {Function} fn wrapped function
@param {Function} callback or wrapped function
sure_result, sure_spread
trap_sure, trap_sure_result - deprecated Since version 0.1.x all functions catch thrown exceprtions
Copyright (c) PushOk Software
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.