Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 20 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

[Generator](http://wiki.ecmascript.org/doku.php?id=harmony:generators)-based control-flow for Node enabling asynchronous code without callbacks, transpiling, or selling your soul.

Suspend is designed to work seamlessly with Node's [callback conventions](#suspendresume), [promises](#promises), and [thunks](#thunks), but is also compatible with code that follows [other conventions](#suspendresumeraw).
Suspend is designed to work seamlessly with Node's [callback conventions](#suspendresume) and [promises](#promises), but is also compatible with code that follows [other conventions](#suspendresumeraw).

*Related reading for the generator-uninitiated: [What's the Big Deal with Generators?](http://devsmash.com/blog/whats-the-big-deal-with-generators)*

**Note:** Generators are a new feature in ES6 and are still hidden behind the `--harmony-generators` (or the more general `--harmony`) flag in V8:
**Note:** Generators are a new feature in ES6. If your node version too early to support it directly, try the `--harmony-generators` (or the more general `--harmony`) flag in V8:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If your node version is too early to support it directly, try


```
$ node --harmony-generators your-script.js
Expand Down Expand Up @@ -37,17 +37,8 @@ suspend(function*() {
})();
```

*Working with thunks:*
~~*Working with thunks:*~~ NOT support thunk since v0.7.0

```javascript
var suspend = require('suspend'),
readFile = require('thunkify')(require('fs').readFile);

suspend(function*() {
var package = JSON.parse(yield readFile('package.json', 'utf8'));
console.log(package.name);
});
```

## Installation

Expand All @@ -58,7 +49,7 @@ $ npm install suspend
## Documentation

* **[API](#api)**
* [suspend.async(fn*)](#suspendasyncfn)
* [suspend.callback(fn*)](#suspendcallbackfn)
* [suspend.promise(fn*)](#suspendpromisefn)
* [suspend.fn(fn*)](#suspendfnfn)
* [suspend.run(fn*)](#suspendrunfn-cb)
Expand All @@ -68,22 +59,24 @@ $ npm install suspend
* [suspend.resume()](#suspendresume)
* [suspend.resumeRaw()](#suspendresumeraw)
* [Promises](#promises)
* [Thunks](#thunks)
* [~~Thunks~~](#thunks)
* **[Parallel Operations](#parallel-operations)**
* [suspend.fork() and suspend.join()](#suspendfork-and-suspendjoin)
* [Combining with Other Control-Flow Libraries](#combining-with-other-control-flow-libraries)
* **[Error Handling](#error-handling)**

## API

### `suspend.async(fn*)`
### `suspend.callback(fn*)`

Same as `suspend.async(fn*)` in previous version.

Accepts a generator function `fn*`, and returns a wrapper function that follows [Node's callback conventions](http://docs.nodejitsu.com/articles/getting-started/control-flow/what-are-callbacks). Note that the wrapper function requires a callback as the last parameter.

**Example:**

```javascript
var readJsonFile = suspend.async(function*(fileName) {
var readJsonFile = suspend.callback(function*(fileName) {
var rawFile = yield fs.readFile(fileName, 'utf8', suspend.resume());
return JSON.parse(rawFile);
});
Expand All @@ -94,7 +87,7 @@ readJsonFile('package.json', function(err, packageData) {
});
```

Note that `.async()` lets you return your final result, instead of having to explicitly accept a callback parameter and pass the result manually. Likewise, any uncaught errors will be passed to the callback as the error argument (see the section on [error handling](#error-handling) for more information).
Note that `.callback()` lets you return your final result, instead of having to explicitly accept a callback parameter and pass the result manually. Likewise, any uncaught errors will be passed to the callback as the error argument (see the section on [error handling](#error-handling) for more information).

---

Expand All @@ -120,7 +113,7 @@ Note that the above example also demonstrates the ability to yield promises, whi

### `suspend.fn(fn*)`

Accepts a generator function `fn*`, and returns a wrapper function that, unlike `.async()`, makes no assumptions regarding callback conventions. This makes `.fn()` useful for event handlers, `setTimeout()` functions, and other use cases that don't expect Node's typical asynchronous method signature.
Accepts a generator function `fn*`, and returns a wrapper function that, unlike `.callback()`, makes no assumptions regarding callback conventions. This makes `.fn()` useful for event handlers, `setTimeout()` functions, and other use cases that don't expect Node's typical asynchronous method signature.

**Note:** As a shorthand convenience, `suspend(fn*)` is an alias for `suspend.fn(fn*)`.

Expand Down Expand Up @@ -166,7 +159,7 @@ The `yield` keyword is a new language feature associated with generators in ES6.

In suspend, as the name implies, we use `yield` to suspend the generator while performing asynchronous operations, and then resume it once the operation is complete.

If you're using [promises](#promises) or [thunks](#thunks), suspend can resume for you automatically. However, given that the majority of the Node ecosystem relies on callbacks, suspend provides some simple mechanisms for interacting with callback-based code: [`resume()`](#suspendresume) and [`resumeRaw()`](#suspendresumeraw).
If you're using [promises](#promises) or [~~thunks~~](#thunks), suspend can resume for you automatically. However, given that the majority of the Node ecosystem relies on callbacks, suspend provides some simple mechanisms for interacting with callback-based code: [`resume()`](#suspendresume) and [`resumeRaw()`](#suspendresumeraw).

---

Expand Down Expand Up @@ -219,11 +212,11 @@ The above is an example of working with [mongoose](http://mongoosejs.com/), whic

---

### Thunks
### ~~Thunks~~ (no longer support)

Thunks provide a nice, lightweight alternative to promises when working with generators. Sometimes referred to as continuables, thunks are simply functions returned from asynchronous operations, that accept a single node-style callback parameter. For creating "thunkified" versions of asynchronous functions, I recommend TJ Holowaychuk's [thunkify](https://github.com/visionmedia/node-thunkify) module.
~~Thunks provide a nice, lightweight alternative to promises when working with generators. Sometimes referred to as continuables, thunks are simply functions returned from asynchronous operations, that accept a single node-style callback parameter. For creating "thunkified" versions of asynchronous functions, I recommend TJ Holowaychuk's [thunkify](https://github.com/visionmedia/node-thunkify) module.~~

**Example:**
**~~Example:~~**

```javascript
var readFile = thunkify(fs.readFile);
Expand All @@ -233,7 +226,7 @@ suspend(function*() {
});
```

As can be seen, one must simply yield the thunk itself and suspend will appropriately handle the eventual result or error. Just like with promises, thunks and suspend make a particularly nice combination, as once again there's no need to pass in `resume()` or other callback mechanism into the async function itself.
~~As can be seen, one must simply yield the thunk itself and suspend will appropriately handle the eventual result or error. Just like with promises, thunks and suspend make a particularly nice combination, as once again there's no need to pass in `resume()` or other callback mechanism into the async function itself.~~

## Parallel Operations

Expand Down Expand Up @@ -300,7 +293,7 @@ suspend.run(function*() {
});
```

While unifying the error handling model is convenient, it can also be tedious to write lots of `try/catches`. For this reason, both `.run()` and `.async()` will automatically pass any unhandled errors to their callbacks. This makes it trivial to write functions using suspend that safely handle errors in accordance with Node's callback conventions:
While unifying the error handling model is convenient, it can also be tedious to write lots of `try/catches`. For this reason, both `.run()` and `.callback()` will automatically pass any unhandled errors to their callbacks. This makes it trivial to write functions using suspend that safely handle errors in accordance with Node's callback conventions:

**Example (`.run()`):**

Expand All @@ -316,7 +309,7 @@ suspend.run(function*() {
});
```

**Example (`.async()`):**
**Example (`.callback()`):**

```javascript
var readJsonFile = suspend.async(function*(fileName) {
Expand All @@ -336,13 +329,13 @@ readJsonFile('package.json', function(err, packageData) {
Here's what's important to remember:

1. If an error occurs, you'll have a chance to capture it with a `try/catch`.
2. If you don't catch an error, *and* you're using `suspend()`, `.async()` or `.run()` with a callback, then the error will be passed to the callback.
2. If you don't catch an error, *and* you're using `suspend()`, `.callback()` or `.run()` with a callback, then the error will be passed to the callback.
3. Otherwise, the unhandled error will be re-thrown globally.


## Versioning and Stability

Please note that generators are currently only supported in unstable (v0.11.x) versions of Node, so it would probably be wise to treat suspend as no more stable than the version of Node that supports it. Also note that suspend follows [SemVer](http://semver.org/) for versioning, so breaking changes will never be introduced in a patch release.
~~Please note that generators are currently only supported in unstable (v0.11.x) versions of Node, so it would probably be wise to treat suspend as no more stable than the version of Node that supports it.~~ Generator has been supported well by node v4.x and above. Also note that suspend follows [SemVer](http://semver.org/) for versioning, so breaking changes will never be introduced in a patch release.

Feedback is greatly appreciated, so if you find anything or have any suggestions, please [open an issue](https://github.com/jmar777/suspend/issues?state=open), [tweet at me](https://twitter.com/jmar777), or shoot me an email (jmar777@gmail.com)!

Expand Down