gen-sync

2.0.0 • Public • Published

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 function
  • 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
  • context: 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 error
  • 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

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/

Versions

Current Tags

  • Version
    Downloads (Last 7 Days)
    • Tag
  • 2.0.0
    1
    • latest

Version History

Package Sidebar

Install

npm i gen-sync

Weekly Downloads

1

Version

2.0.0

License

MIT

Unpacked Size

26.3 kB

Total Files

11

Last publish

Collaborators

  • ml27299