synchronize3
Version:
Turns asynchronous function into synchronous
439 lines (385 loc) • 12.6 kB
HTML
<html lang='en'>
<head>
<meta charset='utf-8'>
<title>synchronize.js</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<meta name='description' content=''>
<meta name='author' content=''>
<!-- Styles -->
<link href='assets/bootstrap/css/bootstrap.css' rel='stylesheet'>
<link href='assets/google-code-prettify/prettify.css' rel='stylesheet'>
<link href='assets/style.css' rel='stylesheet'>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src='http://html5shim.googlecode.com/svn/trunk/html5.js'></script>
<![endif]-->
</head>
<body>
<!-- <div class='navbar navbar-fixed-top'>
<div class='navbar-inner'>
<div class='container'>
<a class='brand' href='#'>synchronize.js</a>
<ul class='nav'>
<li class='active'><a href='#'>Home</a></li>
<li><a href='#about'>About</a></li>
<li><a href='#contact'>Contact</a></li>
</ul>
</div>
</div>
</div> -->
<div class='container'>
<!-- <pre class='prettyprint'>
a = function(){
console.log('hi')
}
</pre> -->
<div class='row'>
<div class='offset1 span10'>
<!-- Overview -->
<div class='page-header'>
<h1>
synchronize.js
<small>write asynchronous code as if it's synchronous</small>
</h1>
</div>
<p>
Use <code>await</code> and <code>defer</code> to execute asynchronous functions
synchronously.
</p>
<p>
Synchronize assumes node-style <code>(err, result)</code> 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 <code>defers</code>.
</p>
<p>
You can use the helper <code>sync(fn)</code> 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.
</p>
<p>
Use <code>await</code>/<code>defer</code> manually to get precise control or automatically to write compact and clean code.
</p>
<p>
Install it with <code>npm install synchronize</code>.
</p>
<p>
The project is hosted on
<a href='http://github.com/alexeypetrushin/synchronize'>GitHub</a>.
<br>
You can report bugs and discuss features on the
<a href='http://github.com/alexeypetrushin/synchronize/issues'>Issues</a> page.
</p>
<!-- How to use -->
<h2>Usage patterns</h2>
<div class='row'>
<div class='span5'>
<pre class='prettyprint'>
data = sync.await(fs.readFile(fname, sync.defer()))
</pre>
</div>
<div class='span5'>
<p>
Use <code>await</code> and <code>defer</code> keywords by hand
to call a function synchronously (you can make await and defer global
to make things shorter).
</p>
</div>
</div>
<div class='row' style="margin-top: 10px">
<div class='span5'>
<pre class='prettyprint'>
sync(fs, 'readFile')
var data = fs.readFile(fname)
fs.readFile(fname, function(err, data)){}
</pre>
</div>
<div class='span5'>
<p>
Use <code>sync()</code> 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.
</p>
</div>
</div>
<div class='row' style="margin-top: 10px">
<div class='span5'>
<pre class='prettyprint'>
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){})
</pre>
</div>
<div class='span5'>
<p>
You can use <code>sync()</code> on a bare function to return
a synchronized version of the function. It can still be used
in asynchronous form.
</p>
</div>
</div>
<!-- Samples -->
<h2>Basic usage, printing content of file</h2>
<div class='row'>
<div class='span5'>
<pre class='prettyprint'>
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)
})
})
</pre>
</div>
<div class='span5'>
<p>
In order to use synchronized functions and pause execution without blocking Node.js we
need to wrap execution into a <code>Fiber</code> using <code>sync.fiber()</code>.
</p>
<p style="margin-top:50px">
Inside of Fiber we can call asynchronous functions as if it's synchronous.
</p>
<p style="margin-top:30px">
We can also use standard try/catch statements to catch asynchronous errors.
</p>
<p style="margin-top:70px">
Or call readFile asynchronously if we wish.
</p>
</div>
</div>
<h2>Listing and printing files in directory</h2>
<p>
Listing content of current directory, checking if path is file and printing its content
to console.
</p>
<div class='row'>
<div class='span5'>
<h3>Using synchronize</h3>
<pre class='prettyprint'>
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)
}
})
</pre>
</div>
<div class='span5'>
<h3>The same code without synchronization</h3>
<pre class='prettyprint'>
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)
})
</pre>
</div>
</div>
<h2>Parallel and serial operations</h2>
<p>
Synchronize.js can run multiple operations in parallel using
<code>sync.parallel()</code>.
</p>
<div class='row'>
<div class='span5'>
<h3>Async operations in serial</h3>
<pre class='prettyprint'>
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]
})
</pre>
</div>
<div class='span5'>
<h3>Async operations in parallel</h3>
<pre class='prettyprint'>
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]
});
</pre>
</div>
</div>
<h2>Multiple arguments in callbacks</h2>
<p>
Synchronize.js accepts multiple arguments in callbacks using <code>sync.defers()</code>.
</p>
<div class='row'>
<div class='span5'>
<h3>Multiple arguments in serial</h3>
<pre class='prettyprint'>
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')))
})
</pre>
</div>
<div class='span5'>
<h3>Multiple arguments in parallel</h3>
<pre class='prettyprint'>
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)
})
</pre>
</div>
</div>
<h2>Usage with Express.js</h2>
<div class='row'>
<div class='span5'>
<pre class='prettyprint'>
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)
</pre>
</div>
<div class='span5'>
<p>Synchronized code can be mixed with asynchronous code in any combination.</p>
<p>
Here is one possible way to use it with Express.js.</p>
</div>
</div>
<h2>Usage with Mocha.js</h2>
<div class='row'>
<div class='span5'>
<pre class='prettyprint'>
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')
}))
})
</pre>
</div>
<div class='span5'>
<p>
You can use the <code>sync.asyncIt</code> helper to greatly simplify writing
asynchronous tests.
</p>
<p>
You may also take a look at
<a href='https://github.com/alexeypetrushin/mongo-lite/blob/master/test/collection.coffee'>
this real-life test scenario</a>
that uses synchronize to simplify asynchronous calls for MongoDB.
</p>
</div>
</div>
<!-- How to use -->
<h2>Similar tools</h2>
<p>
<a href="http://monojs.org">MonoJS</a> - synchronous web framework based on synchronize.
</p>
<hr/>
<p>Copyright <a href='http://petrush.in'>Alexey Petrushin</a>, released under MIT License</p>
</div>
</div>
</div><!-- /container -->
<a href='http://github.com/alexeypetrushin/synchronize'><img style='position: absolute; top: 0; right: 0; border: 0; z-index: 2080;' src='https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png' alt='Fork me on GitHub'></a>
<!-- Scripts -->
<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'></script>
<script src='assets/bootstrap/js/bootstrap.js'></script>
<script src='assets/google-code-prettify/prettify.js'></script>
<script>
$(prettyPrint)
</script>
</body>
</html>