{"id":17862138,"url":"https://github.com/jtblin/syncho","last_synced_at":"2025-09-24T09:31:50.072Z","repository":{"id":13249555,"uuid":"15934460","full_name":"jtblin/syncho","owner":"jtblin","description":"Fast and lean abstraction for node Fibers. Easily run asynchronous functions synchronously.","archived":false,"fork":false,"pushed_at":"2015-07-28T08:48:58.000Z","size":221,"stargazers_count":21,"open_issues_count":3,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-13T03:22:00.593Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jtblin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-01-15T12:23:34.000Z","updated_at":"2020-12-10T13:51:57.000Z","dependencies_parsed_at":"2022-08-25T18:12:03.806Z","dependency_job_id":null,"html_url":"https://github.com/jtblin/syncho","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtblin%2Fsyncho","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtblin%2Fsyncho/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtblin%2Fsyncho/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtblin%2Fsyncho/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jtblin","download_url":"https://codeload.github.com/jtblin/syncho/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234067126,"owners_count":18774200,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-28T08:50:43.035Z","updated_at":"2025-09-24T09:31:44.780Z","avatar_url":"https://github.com/jtblin.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![NPM version](https://badge.fury.io/js/syncho.svg)](http://badge.fury.io/js/syncho)\n\n# syncho\n\n`syncho` is a thin and fast wrapper around [fibers](https://github.com/laverdet/node-fibers) for node.js.\nThe API is very similar to [node-sync](http://github.com/0ctave/node-sync)\nbut optimized to reduce overhead (see benchmarks) and in less than 100 lines of code.\n\n\n## Usage\n\n    npm install syncho\n\n## API\n\n### Sync(fn)\n\n  Pass a function `fn` to run in a fiber. This is just a shortcut to `Fiber(fn).run()`.\n  Synchronized functions will throw in case of error so you can use standard `try` and `catch`\n  to run your code. All `syncho` methods need to be run inside a fiber.\n\n```js\nvar Sync = require('syncho');\n\nSync(function (){\n  try {\n    // run code synchronously inside fiber\n  }\n  catch (e) {\n    // handle error\n  }\n});\n```\n\n### Function.prototype.sync(thisArg, args)\n\n  Execute an asynchronous function inside a fiber, and return the result or throw in case of error.\n  Use the idiomatic `Function.prototype.call` signature where the first argument is the object you want\n  to bind the function to, the others are the standard arguments of the function.\n\n```js\nvar Sync = require('syncho');\nvar redis = require('redis');\nvar db = redis.createClient();\n\nfunction asyncEcho (text, cb) {\n  process.nextTick(function () {\n    cb(null, text);\n  });\n}\n\nfunction asyncError (text, cb) {\n  process.nextTick(function () {\n    cb('Too bad');\n  });\n}\n\nSync(function (){\n  try {\n\n    // simple aync function\n\n    console.log('Before sync');\n    console.log(asyncEcho.sync(null, 'Hello!'));\n    console.log('After sync');\n\n    // async function binding object\n\n    db.set.sync(db, 'foo', 'bar');\n    console.log(db.get.sync(db, 'foo'));\n\n    // this will throw an error\n\n    asyncError.sync(null, 'Hello!');\n\n  }\n  catch (e) {\n    console.error(e);\n  }\n});\n```\n\n### Function.prototype.future(thisArg, args)\n\n  Execute an asynchronous function asynchronously and return a future, an object with a unique `wait` function, which will\n  return the result or throw an error when `wait` is called. Behind the scene, `Function.prototype.sync`\n  is just a shortcut to `fn.future(...).wait()`. Futures can be used to execute code in parallel. Note that the function\n  starts executing as soon as the future is created. When `wait` is called, if the asynchronous function\n  has not returned results yet, then and only then it will `yield` the fiber, otherwise it will just return the result\n  or throw.\n\n```js\nvar fs = require('fs');\n\nSync(function (){\n  try {\n\n    var a = fs.stat.future(null, 'package.json');\n    var b = fs.stat.future(null, '.gitignore');\n    var c = fs.stat.future(null, 'README.md');\n\n    console.log(a.wait().size);\n    console.log(b.wait().size);\n    console.log(c.wait().size);\n\n  }\n  catch (e) {\n    console.error(e);\n  }\n});\n```\n\n### Function.prototype.async()\n\n  Return a function that accepts a callback to run a synchronous function asynchronously. This can be useful to pass\n  functions that use fibers to external APIs that are not fiber aware and need to run code asynchronously. You can\n  also use this to make parallel executions of synchronous functions by calling the `future` method\n  on the now asynchronous function.\n\n```js\nvar Sync = require('syncho');\nvar redis = require('redis');\nvar db = redis.createClient();\n\nfunction syncGet (key) {\n  return db.get.sync(db, key);\n}\n\nSync(function (){\n  try {\n\n    // Just putting some data\n    db.set.sync(db, 'foo', 'bar');\n    db.set.sync(db, 'bar', 'baz');\n    db.set.sync(db, 'baz', 'foo');\n\n    // making our sync function async\n    var asyncGet = syncGet.async();\n\n    // execute in parallel\n    var foo = asyncGet.future(db, 'foo');\n    var bar = asyncGet.future(db, 'bar');\n    var baz = asyncGet.future(db, 'baz');\n\n    console.log(foo.wait(), bar.wait(), baz.wait());\n\n  }\n  catch (e) {\n    console.error(e);\n  }\n});\n```\n\n### Function.prototype.wrap()\n\n  Wrap the function in a Fiber. Useful for events emitters and other functions that expect a function as an argument.\n\n\n```js\n   syncSubjectUnderTest().should.have.been.run.in.a.Fiber;\n```\n\n### Function.prototype.wrapIt()\n\n  Wrap a function in a Fiber passing `done` callback for [mocha](https://github.com/mochajs/mocha) tests.\n\nIn setup.js\n\n    var Sync = require('syncho');\n\nIn your tests:\n\n```js\n  it('runs the test in a fiber', function (done) {\n    syncSubjectUnderTest().should.have.been.run.in.a.Fiber;\n    done();\n  }.wrapIt());\n```\n\n### Sync.Fiber(fn)\n\n  Expose the original `Fiber` object in case you need it.\n\n### Sync.sleep(ms)\n\n  Sleep for number of milliseconds (default 1000).\n\n```js\nvar Sync = require('syncho');\n\nSync(function (){\n  try {\n\n    console.time('sleep');\n    Sync.sleep(100);\n    console.timeEnd('sleep');\n\n  }\n  catch (e) {\n    console.error(e);\n  }\n});\n```\n\n### Sync.middleware()\n\n  Middleware for express. Run each request in a fiber so that you can easily use sync methods.\n\n```js\nvar Sync = require('syncho');\nvar express = require('express'), app = express();\nvar fs = require('fs');\n\napp.use(express.query());\napp.use(Sync.middleware());\n\napp.get('/', function (req, res) {\n  var file = fs.readFile.sync(null, req.query.file);\n  res.end(file);\n});\n\napp.listen(3000, function () {\n  console.log('Express server listening on port 3000');\n});\n```\n\n## Why `syncho` and comparison with other fibers abstraction modules\n\n  Fibers are awesome. I am not going to discussed their value here as it has been thoroughly documented but going to focus\n  on why a new module was needed when there is already a ton of existing abstractions.\n\n  I really loved the simplicity of the `sync` module from @0ctave compared to the original `Future` abstraction\n  and other `Fiber` modules but there are some performance issues especially with `Function.prototype.future` in\n  the `sync` module and code like statistics that I think unnecessary (500 loc for `sync` vs 100 loc for `syncho`).\n  As this isn't a fork of the `sync` module at all (code is written entirely from scratch) and is not 100% compatible\n  with its API [1], it was better to create a brand new module.\n\n  Compare the different APIs (based on code already running in a fiber for simplicity)\n\n    // Raw fibers\n    var Fiber = require('fibers');\n    var fiber = Fiber.current;\n    db.get('foo', function (err, res) {\n      if (err) fiber.throw(err);\n      else fiber.run(res);\n    });\n    Fiber.yield();\n\n    // Future\n    var Future = require('fibers/future');\n    var future = Future.wrap(db.get.bind(db));\n    var res = future('foo').wait();\n\n    // synchronize\n    var sync = require('synchronize');\n    sync(db, 'get');\n    var res = db.get('foo');\n\n    // syncho\n    var res = db.get.sync(db, 'foo'); // Sync only need to be required once in your project / module to run the code in the fiber\n\n  Also note that `syncho` does not decorate the fiber or any of the objects or functions that you sync and the\n  only additions are the methods added to `Function.prototype`.\n\n  (1) API for futures is not compatible with the `sync` module due to the creation of multiple properties on each future\n  which has quite an impact on performance. I chose a simple `wait` function as with Marcel's original future implementation.\n\n### ES6 generators and coroutines\n\nWith ES6 generators and coroutines, Fibers may become obsolete but until there is a stable node version released\nsupporting them, the only way to run asynchronous code synchronously is using fibers. Also the `yield` and `*` keywords\nare a bit intrusive and the `sync` API is still the more elegant IMO. It will be interesting to see benchmarks with\nthe stable version of node for comparison. At the moment (0.11.9), 'syncho' with fibers is faster than\n[co](https://github.com/visionmedia/co) with ES6 generators.\n\n    syncho-simple: 12647ms\n    syncho-object: 12793ms\n    co-simple: 13850ms\n    co-object: 14643ms\n\n## Benchmarks\n\nNB: all the existing Fibers modules are in general pretty fast and the cost of fibers is marginal. The longer the IO takes,\nthe more marginal it becomes to the point of being insignificant.\n\nTL;DR `syncho` is the fastest (from 5% to 50 times faster than `sync`) of all fibers modules on every benchmark, and almost\nas fast as using asynchronous code except when running synchronous functions in parallel. It is even faster than using\nthe `async` module (I haven't tested other asynchronous control-flow modules) and often faster than using raw fibers directly.\n\n### Simple function (10,000)\n\n    synchronize: 12905ms\n    syncho: 12750ms\n    syncho-future: 12712ms\n    sync: 13265ms\n    fibrous: 14171ms\n    fibers: 13074ms\n    future: 14212ms\n    async.series: 13181ms\n    asyncFn: 12872ms\n\n### Binding to an object (10,000)\n\n    synchronize: 12939ms\n    syncho: 12684ms\n    sync: 13502ms\n    fibrous: 14449ms\n    fibers: 13381ms\n    future: 14497ms\n    async.series: 13322ms\n    asyncFn: 12990ms\n\n### Parallel processing with futures (100,000)\n\n    sync-future: 8697ms\n    syncho-future: 755ms\n    async.parallel: 2331ms\n    future: 2004ms\n\n### Parallel processing of synced functions with async and futures (10,000)\n\n    syncho: 486ms\n    async.parallel: 307ms\n    sync: 23981ms\n\n## Contributions\n\nPlease open issues for bugs and suggestions in [github](https://github.com/jtblin/syncho/issues).\nPull requests with tests are welcome.\n\n## Author\n\nJerome Touffe-Blin, [@jtblin](https://twitter.com/jtlbin), [About me](http://about.me/jtblin)\n\n## License\n\nsyncho is copyright 2013 Jerome Touffe-Blin and contributors. It is licensed under the BSD license. See the include LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjtblin%2Fsyncho","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjtblin%2Fsyncho","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjtblin%2Fsyncho/lists"}