/node-sync

Write simple and readable synchronous code in nodejs using fibers

Primary LanguageJavaScriptMIT LicenseMIT

Introduction

node-sync is a simple library that allows you to call any asynchronous function in synchronous way. The main benefit is that it uses javascript-native design - Function.prototype.sync function, instead of heavy APIs which you'll need to learn. Also, asynchronous function which was called synchronously through node-sync doesn't blocks the whole process - it blocks only current thread!

It built on node-fibers library as a multithreading solution.

Examples

Simply call asynchronous function synchronously:

var Sync = require('sync');

function asyncFunction(a, b, callback) {
	process.nextTick(function(){
		callback(null, a + b);
	})
}

// Run in a fiber
Sync(function(){
	
	// Function.prototype.sync() interface is same as Function.prototype.call() - first argument is 'this' context
	var result = asyncFunction.sync(null, 2, 3);
	console.log(result); // 5

	// Read file synchronously without blocking whole process? no problem
	var source = require('fs').readFile.sync(null, __filename);
    console.log(String(source)); // prints the source of this example itself
})

It throws exceptions!

var Sync = require('sync');

function asyncFunction(a, b, callback) {
	process.nextTick(function(){
		callback('something went wrong');
	})
}

// Run in a fiber
Sync(function(){

	try {
		var result = asyncFunction.sync(null, 2, 3);
	}
	catch (e) {
		console.error(e); // something went wrong
	}
})

// Or simply specify callback function for Sync fiber
// handy when you use Sync in asynchronous environment
Sync(function(){
	
	// The result will be passed to a Sync callback
	var result = asyncFunction.sync(null, 2, 3);
	return result;
	
}, function(err, result){ // <-- standard callback
	
	if (err) console.error(err); // something went wrong
	
	// The result which was returned from Sync body function
	console.log(result);
})

Transparent integration

var Sync = require('sync');

var MyNewFunctionThatUsesFibers = function(a, b) { // <-- no callback here
	
	// we can use yield here
	// yield();
	
	// or throw an exception!
	// throw new Error('something went wrong');
	
	// or even sleep
	// Sync.sleep(200);
	
	// or turn fs.readFile to non-blocking synchronous function
	// var source = require('fs').readFile.sync(null, __filename)
	
	return a + b; // just return a value
	
}.async() // <-- here we make this function friendly with async environment

// Classic asynchronous nodejs environment
var MyOldFashoinAppFunction = function() {
	
	// We just use our MyNewFunctionThatUsesFibers normally, in a callback-driven way
	MyNewFunctionThatUsesFibers(2, 3, function(err, result){
		
		// If MyNewFunctionThatUsesFibers will throw an exception, it will go here
		if (err) return console.error(err);
		
		// 'return' value of MyNewFunctionThatUsesFibers
		console.log(result); // 5
	})
}

// From fiber environment
Sync(function(){
	
	// Run MyNewFunctionThatUsesFibers synchronously
	var result = MyNewFunctionThatUsesFibers(2, 3);
	console.log(result); // 5
	
	// Or use sync() for it (same behavior)
	var result = MyNewFunctionThatUsesFibers.sync(null, 2, 3);
	console.log(result); // 5
})

Parallel execution:

var Sync = require('sync'),
	Future = Sync.Future();

// Run in a fiber
Sync(function(){
	try {
		// Three function calls in parallel
		var foo = asyncFunction.future(null, 2, 3);
		var bar = asyncFunction.future(null, 5, 5);
		var baz = asyncFunction.future(null, 10, 10);
	
		// We are immediately here, no blocking
	
		// foo, bar, baz - our tickets to the future!
	    console.log(foo); // { [Function: Future] result: [Getter], error: [Getter] }
	
		// Get the results
		// (when you touch 'result' getter, it blocks until result would be returned)
		console.log(foo.result, bar.result, baz.result); // 5 10 20
	
		// Or you can straightly use Sync.Future without wrapper
		// This call doesn't blocks
		asyncFunction(2, 3, foo = Future());
	
		// foo is a ticket
	    console.log(foo); // { [Function: Future] result: [Getter], error: [Getter] }

		// Wait for the result
		console.log(foo.result); // 5
	}
	catch (e) {
		// If some of async functions returned an error to a callback
		// it will be thrown as exception
		console.error(e);
	}
})

Timeouts support

var Sync = require('sync'),
	Future = Sync.Future;

function asyncFunction(a, b, callback) {
	setTimeout(function(){
		callback(null, a + b);
	}, 1000)
}

// Run in a fiber
Sync(function(){
	
	// asyncFunction returns the result after 1000 ms
	var foo = asyncFunction.future(null, 2, 3);
	// but we can wait only 500ms!
	foo.timeout = 500;

	try {
	    var result = foo.result;
	}
	catch (e) {
	    console.error(e); // Future function timed out at 500 ms
	}

	// Same example with straight future function
	asyncFunction(2, 3, foo = new Future(500));

	try {
	    var result = foo.result;
	}
	catch (e) {
	    console.error(e); // Future function timed out at 500 ms
	}
})

How to address non-uniform callbacks

Sometimes third-party libraries are not following convention and passing multiple result parameters to the callback, e.g. callback(err, recordsets, returnValue). In this situation, node-sync will simply return array of values instead of value.

// Asynchronous which returns multiple arguments to a callback and returning a value synchronously
function asyncFunctionReturningMultipleArguments(callback) {
    process.nextTick(function(){
        callback(null, 2, 3);
    })
}

Sync(function(){
    var result = asyncFunctionReturningMultipleArguments.sync();
    assert.equal(result, [2, 3]);
})

See more examples in examples directory.

Installation

install

$ npm install sync

and then

$ node your_file_using_sync.js