Use await
and defer
to execute asynchronous functions
synchronously.
Synchronize assumes node-style (err, result)
callbacks.
The second parameter given to the cb will be returned.
If an error is present it will be thrown and can be intercepted by a try/catch statement.
Multiple and named arguments are supported with defers
.
You can use the helper sync(fn)
and it will use await/defer
automatically.
The helper is non-destructive and the function can still be called asynchronously
if called with a callback.
You can also mix it with other synchronous or asynchronous code.
Use await
/defer
manually to get precise control or automatically to write compact and clean code.
Install it with npm install synchronize
.
The project is hosted on
GitHub.
You can report bugs and discuss features on the
Issues page.
data = sync.await(fs.readFile(fname, sync.defer()))
Use await
and defer
keywords by hand
to call a function synchronously (you can make await and defer global
to make things shorter).
sync(fs, 'readFile') var data = fs.readFile(fname) fs.readFile(fname, function(err, data)){}
Use sync()
to make an async function synchronize.js-aware.
You can use it in both ways - synchronous or asynchronous.
Basically it uses the same await/defer pattern, but automatically.
This object form preserves context, unlike the form below.
asyncFn = sync(asyncFn) var data = asyncFn(input) // You can still use the async form of this fn var data = sync.await(asyncFn(input, sync.defer())) asyncFn(input, function(err, data){})
You can use sync()
on a bare function to return
a synchronized version of the function. It can still be used
in asynchronous form.
var sync = require('synchronize') var fs = require('fs') sync(fs, 'readFile') sync.fiber(function(){ var data = fs.readFile(__filename, 'utf8') console.log(data) try { data = fs.readFile('invalid', 'utf8') } catch (err) { console.log(err) } fs.readFile(__filename, 'utf8', function(err, data){ console.log(data) }) })
In order to use synchronized functions and pause execution without blocking Node.js we
need to wrap execution into a Fiber
using sync.fiber()
.
Inside of Fiber we can call asynchronous functions as if it's synchronous.
We can also use standard try/catch statements to catch asynchronous errors.
Or call readFile asynchronously if we wish.
Listing content of current directory, checking if path is file and printing its content to console.
var sync = require('synchronize') var fs = require('fs') sync(fs, 'readdir', 'stat', 'readFile') sync.fiber(function(){ var i, paths, path, stat, data paths = fs.readdir('.') for(i = 0; i < paths.length; i++){ path = paths[i] stat = fs.stat(path) if(!stat.isFile()) continue data = fs.readFile(path, 'utf8') console.log(data) } })
var fs = require('fs') var printFile = function(paths, i){ if(i >= paths.length) return var path = paths[i] fs.stat(path, function(err, stat){ if(err) throw err if(stat.isFile()){ fs.readFile(path, 'utf8', function(err, data){ if(err) throw err console.log(data) printFile(paths, i + 1) }) } else { printFile(paths, i + 1) } }) } fs.readdir('.', function(err, paths){ if(err) throw err printFile(paths, 0) })
Synchronize.js can run multiple operations in parallel using
sync.parallel()
.
var sync = require('synchronize') var read = function(a, cb) { setTimeout(function(){ // remember that callbacks expect (err, result) cb(null, a) }, 1000) } // We can use the `sync` helper here to avoid using // `await()` and `defer()` manually. read = sync(read); sync.fiber(function() { var results = [] results.push(read(1))) results.push(read(2))) // results now contains [1,2] })
var sync = require('synchronize') function read(a, cb) { setTimeout(function(){ cb(null, a) }, 1000) } // Runs in parallel sync.fiber(function() { sync.parallel(function() { // You must call defer() manually within // a parallel operation. read(1, sync.defer()) read(2, sync.defer()) }); var results = sync.await() // results now contains [1,2] });
Synchronize.js accepts multiple arguments in callbacks using sync.defers()
.
var sync = require('synchronize') function read(a, b, c, cb) { setTimeout(function(){ cb(null, a, b, c) }, 1000) } sync.fiber(function() { // Returns [1,2,3] var results = sync.await( read(1, 2, 3, sync.defers())) // Returns {a: 4, b: 5, c: 6} var namedResults = sync.await( read(4, 5, 6, sync.defers('a', 'b', 'c'))) })
var sync = require('synchronize') function read(a, b, c, cb) { setTimeout(function(){ cb(null, a, b, c) }, 1000) } sync.fiber(function() { sync.parallel(function(){ read(1, 2, 3, sync.defers()) read(4, 5, 6, sync.defers()) }); // returns [[1,2,3],[4,5,6]] var results = sync.await() // concat to get [1,2,3,4,5,6] results = [].concat.apply([], results) })
var sync = require('synchronize') var fs = require('fs') var express = require('express') sync(fs, 'readFile') var app = express.createServer() app.use(function(req, res, next){ sync.fiber(next) }) app.get('/', function(req, res){ var data = fs.readFile(__filename, 'utf8') res.send(data, {'Content-Type': 'text/plain'}) }) app.listen(3000)
Synchronized code can be mixed with asynchronous code in any combination.
Here is one possible way to use it with Express.js.
var sync = require('synchronize') var fs = require('fs') sync(fs, 'readFile') var async = sync.asyncIt describe('File System', function(){ it('should read file', async(function(){ var data = fs.readFile(__filename, 'utf8') })) })
You can use the sync.asyncIt
helper to greatly simplify writing
asynchronous tests.
You may also take a look at this real-life test scenario that uses synchronize to simplify asynchronous calls for MongoDB.
MonoJS - synchronous web framework based on synchronize.
Copyright Alexey Petrushin, released under MIT License