gen-sync
v2.0.0
Published
Library that makes simple to run asynchronous functions in synchronous manner, using generators.
Downloads
18
Maintainers
Readme
Introduction
gen-sync is a simple library that allows you to call any asynchronous function in a synchronous way using generators introduced in nodejs 4.X.X and above. This library is lightweight, non blocking, and requires no changes to existing asynchronous functions to use.
Sync Reference
sync([function run], continue, context)
return the response of the asyncronous function
[function run]
: a function used to execute a target asyncronous functioncontinue
: a boolean to determin if the whole Sync process should exit, default is false. When an error occurs an event is triggered, this is where the error is handled. This is the default behavior in gen-sync, if you'd like to overwrite this behavior, set this parameter to truecontext
: this is a class instance used to bind to the target asyncronous function (optional)
async.each([[function async]], [function gather], continue)
[[function async]]
: a list of asyncronous functions to run in parallel[function gather]
: a function that is executed when the results of one of the async functions returns (optional)continue
: a boolean to determin if the whole Sync process should exit, default is false. When an error occurs an event is triggered, this is where the error is handled. This is the default behavior in gen-sync, if you'd like to overwrite this behavior, set this parameter to true
cb
A generic callback you can use in any target asyncronous function, its tied to the current gen-sync instance (see example below)
throw(err, _continue)
used to manually emit an error (see examples below)
err
: a string or Error instance represtending the errorcontinue
: a boolean to determin if the whole Sync process should exit, default is false. When an error occurs an event is triggered, this is where the error is handled. This is the default behavior in gen-sync, if you'd like to overwrite this behavior, set this parameter to true
next(response)
used to manually trigger the next yield execution (see examples below)
Sync events
.on('err', function(err){})
When an error occurs within an asyncronous function, the process is stopped and an error is emmitted to this event
async.each events
.on('data', function(data, index){})
When an asyncronous function results come in, this event is emitted
.on('end', function(data, index){})
When all asyncronous functions are complted, this even is emitted
Basic Usage (See "Error Handling" before getting started)
var Sync = require('gen-sync')
asyncfunction1.js
function asyncfunction1(cb){
setTimeout(function(){ return cb(null, 'my response!') }, 100)
}
index.js
Sync(function *(){
//the this.cb is utilized in this example, it should be the callback of the async function
var response = yield asyncfunction1(this.cb)
response = response[1]
console.log(response) //my response!
//An alternative way of doing the same thing can look like this, since the last argument is the callback, gen-sync will handle it behind the scenes
var response = yield this.sync(asyncfunction1)
response = response[1]
console.log(response) //my response!
})
asyncfunction2.js
function asyncfunction2(param, cb){
setTimeout(function(){ return cb(null, param) }, 100)
}
index.js
Sync(function *(){
//the this.cb is utilized in this example, it should be the callback of the async function
var response = yield asyncfunction2('my response!', this.cb)
response = response[1]
console.log(response) //my response!
//An alternative way of doing the same thing can look like this
var response = yield this.sync(function(cb){ asyncfunction2('my response!', cb) })
response = response[1]
console.log(response) //my response!
})
asyncfunction3.js
function asyncfunction2(param1, cb, param2){
setTimeout(function(){ return cb(null, param1, param2()) }, 100)
}
index.js
Sync(function *(){
//the this.cb is utilized in this example, it should be the callback of the async function
var some_function = function(){ return 'some value!' }
var response = yield asyncfunction3('my response!', this.cb, some_function)
console.log(response[1]) //my response!
console.log(response[2]) //some value!
//An alternative way of doing the same thing can look like this
var response = yield this.sync(function(cb){ asyncfunction3('my response!', cb, some_function) })
response = response[1]
console.log(response) //my response!
})
Class.js
function Class(){
if(this instanceof Class == false) new Class()
this.foo = 'bar'
}
Class.prototype.method = function(cb){
var self = this
setTimeout(function(){ return cb(null, self.foo) }, 100)
}
Sync(function *(){
//.run messes with inheritance, so pass in the context when needed
var c = new Class()
var response = yield c.method(this.cb)
response = response[1]
console.log(response) //bar
//An alternative way of doing the same thing can look like this
var response = yield this.sync(c.method, c)
response = response[1]
console.log(response) //bar
})
Manually handle async function with "Sync.next"
Sync(function *(){
var sync = this
function asyncfunction(){
setTimeout(function(){ return sync.next('my response!') }, 100)
}
//since the only argument is the callback, gen-sync will handle it behind the scenes
var response = yield asyncfunction()
console.log(response) //my response!
})
Error Handling
Errors are handled in multiple ways in gen-sync, the default behavior is to emit the error and stop any further execution of code within the Sync process. The library knows if an error occured if one of the arguments returned from an async function is an instance of the internal "Error" class within nodejs.
There are ways to overwrite this default behavior to suit ones needs, see below
Sync(function *(){
this.on('err', function(err){ console.log(err) //my error! })
function asyncfunction(cb){
setTimeout(function(){ return cb(Error('my error!')) }, 100)
}
var response = yield asyncfunction(this.cb) //this Sync instance exits right here, nothing below this line gets executed
response = response[1]
})
To override
Sync(function *(){
this.on('err', function(err){ console.log(err) //my error! }) //dont need the event handler, but the error still is emmitted
function asyncfunction(cb){
setTimeout(function(){ return cb(Error('my error!')) }, 100)
}
var response = yield asyncfunction(this.cb)
/*continues*/
console.log(response[0]) //my error!
}, true)
Becarefull if the the error returned is not an instance of the Error class in nodejs, gen-sync wont know an error happened and will continue
Sync(function *(){
this.on('err', function(err){ console.log(err) }) //wont be triggered
function asyncfunction(cb){
setTimeout(function(){ return cb('my error!') }, 100)
}
var response = yield this.sync(asyncfunction)
/*continues*/
console.log(response[0]) //my error!
})
Manually handle errors with "Sync.throw" example 1
Sync(function *(){
this.on('err', function(err){ console.log(err) //my error! })
var sync = this
function asyncfunction(){
setTimeout(function(){ return sync.throw('my error!') }, 100)
}
//since the only parameter is the callback and using this.throw
var response = yield asyncfunction() //this Sync instance exits right here, nothing below this line gets executed
})
Manually handle errors with "Sync.throw" example 2
Sync(function *(){
this.on('err', function(err){ console.log(err) //my error! })
var sync = this
function asyncfunction(){
setTimeout(function(){ return sync.throw('my error!', true) }, 100)
}
//since the only parameter is the callback and using this.throw
var response = yield asyncfunction()
/*continues*/
console.log(response[0]) //my error!
})
Manually handle errors with "Sync.throw" example 3
Sync(function *(){
this.on('err', function(err){ console.log(err) //my error! })
var sync = this
function asyncfunction(cb){
setTimeout(function(){ return cb(Error('my error!')) }, 100)
}
//since the only parameter is the callback and using this.throw
var response = yield asyncfunction(this.cb)
/*continues*/
yeild this.throw(response[0], true) //throws error to event handler but doesnt stop
yeild this.throw(response[0]) //throws error to event handler and Sync instance exits right here, nothing below this line gets executed
}, true)
Async Usage
Conditional loops
"Yield" is only available in a generator scope, keep this in mind when using in conjuction with conditional loops
This will crash the script
Sync(function *(){
var self = this
this.on('err', function(err){ /*Handle error here*/ })
function asyncfunction(item, cb){
db.collection.findOne(item).exec(cb)
}
var ids = [{ _id : 0 }, { _id : 1 }, { _id : 2 }]
ids.forEach(function(item){
var response = yield asyncfunction(item, self.cb)
})
})
instead do
Sync(function *(){
this.on('err', function(err){ /*Handle error here*/ })
function asyncfunction(item, cb){
db.collection.findOne(item).exec(cb)
}
var ids = [{ _id : 0 }, { _id : 1 }, { _id : 2 }]
for(var i = 0; i < ids.legth; i++){
var response = yield asyncfunction(ids[i], this.cb)
}
})
Async in Sync
Making things synchronous is awesome and can make code much more readable, but what's so great about javascript is it's asynchronous nature, sometimes we want to run multiple asynchronous functions at the same time, because we don't care what async function finishes first, we just care when a group of async functions are done executing.
Sync(function *(){
this.on('err', function(err){ /*Handle error here*/ })
var result0, result1, result2
function gather(data, index){
if(index == 0) result0 = data[1]
if(index == 1) result1 = data[1]
if(index == 2) result2 = data[1]
}
yield this.async.each([
db.collection0.find().exec,
db.collection1.find().exec,
db.collection2.find().exec
], gather)
console.log(result0) //results from collection0
console.log(result1) //results from collection1
console.log(result2) //results from collection2
})
Alternatively
Sync(function *(){
this.on('err', function(err){ /*Handle error here*/ })
var result0, result1, result2
this.on('data', function(data, index){
if(index == 0) result0 = data[1]
if(index == 1) result1 = data[1]
if(index == 2) result2 = data[1]
})
yield this.async.each([
db.collection0.find().exec,
db.collection1.find().exec,
db.collection2.find().exec
])
console.log(result0) //results from collection0
console.log(result1) //results from collection1
console.log(result2) //results from collection2
})
process.nextTick
Sync(function *(){
yield this.nextTick()
})
https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/