asynco

Make async programming enjoyable.

Usage no npm install needed!

<script type="module">
  import asynco from 'https://cdn.skypack.dev/asynco';
</script>

README

asynco

Asynco provides a coroutine-based solution for JavaScript asynchronous programming, its basic features are as follows:

  • Universal: Supports but doesn't rely on Promise, all the callback APIs(e.g., fs.readFile) can be used directly in coroutines.
  • Performant: Excellent performance, see about performance.
  • Flexible: Coroutines are cancellable, can have timeout, can be started in multiple ways, etc.
  • Easy-to-use: Pays attention to the conciseness and reasonability of usage and APIs, keeps the basic behavior consistent with async function, fully compatible with async function.
  • Lightweight: 8.4kb min, no dependencies.

Compatibility

It is compatible with all JavaScript environments supporting ECMAScript 5 and GeneratorFunction. The latter can be supported natively or by transpilers such as babel and regenerator.

In the dist/ directory you will find the UMD build(asynco.js), the ES Module build(asynco.esm.js) and their compressed builds respectively.

Installation

Via npm

$ npm i asynco --save

Via yarn

$ yarn add asynco

Via cdn

https://unpkg.com/asynco/dist/asynco.js

API docs

Quick start

Step 1: Preparation

var asynco = require('asynco');  // or `import asynco from 'asynco';`

// if you want to use callback APIs directly in coroutines, just creat a callback proxy, then use it everywhere
var cb = asynco.callbackProxy();

// extend Generator in order to get the best programming experience, this is optional
asynco.extendGenerator();

Step 2: Define a coroutine function

Just define a generator function with */yield, in almost the same way of deining an async function with async/await:

function* demo() {
  // returns a single value
  var data = yield fs.readFile('/somefile', 'utf8', cb());

  // returns multiple value
  var [stdout, stderr] = yield child_process.exec('ls /home', cb('all'));

  // returns raw arguments of the callback, now you get a way to handle errors without `try/catch`
  var [err, stat] = yield fs.stat('/somefile', cb('raw'));

  if (!err && stat.isFile()) {
    console.log('It is a file.');
  }

  // returns a boolean value indicating whether the async operation succeed or not
  if (yield fs.access('/somefile', cb('bool'))) {
    console.log('The file is accessible.');
  }

  // use with 3rd-party control flow packages seamlessly, e.g., async
  var contents = yield async.mapLimit(['file1', 'file2', 'file3'], 2, (file, callback) => {
    fs.readFile(file, 'utf8', callback);
  }, cb());

  // yield promises
  var value = yield Promise.resolve('value');

  async function welcome(name) {
    return `Welcome, ${name}`;
  }

  console.log(yield welcome('Sherry'));  // Welcome, Sherry

  // yield generators
  function* hello(name) {
    return `Hello, ${name}`;
  }

  console.log(yield hello('Sherry'));  // Hello, Sherry

  return 'whatever';
}

Setp 3: Start a coroutine

The extended Generator has several instance methods for coroutine manipulation:

// start it with a callback
demo('some', 'args').end((err, ret) => {
  if (err) throw err;
  console.log(ret);  // 'whatever'
});

// use it as a thenable object
demo('some', 'args').then(ret => {
  // ...
}).catch(err => {
  // ...
});

// use it in an async function
async function test() {
  await demo('some', 'args');
}

If you don't want to extend Generator globally, here is an alternative:

var demo = asynco(function* () {
  // ...
});

demo().end((err, ret) => {
  // ...
});

About performance

  • As you can see, asynco fully supports Promise, but it is obviously more efficient and memory-saving when using basic callback APIs.
  • Even if use Promise completely, asynco still has almost the same performance as async function in the test of recent releases of node.js.

License

MIT