|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- # release-zalgo
-
- Helps you write code with promise-like chains that can run both synchronously
- and asynchronously.
-
- ## Installation
-
- ```console
- $ npm install --save release-zalgo
- ```
-
- ## Usage
-
- If you use this module, you'll release **Ẕ̶̨̫̹̌͊͌͑͊̕͢͟a̡̜̦̝͓͇͗̉̆̂͋̏͗̍ͅl̡̛̝͍̅͆̎̊̇̕͜͢ģ̧̧͍͓̜̲͖̹̂͋̆̃̑͗̋͌̊̏ͅǫ̷̧͓̣͚̞̣̋̂̑̊̂̀̿̀̚͟͠ͅ**. You mustn't do that.
-
- Before you proceed, please read this great post by [Isaac
- Schlueter](http://izs.me/) on [Designing APIs for
- Asynchrony](http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony).
-
- The first rule of using this package is to keep your external API consistent.
-
- The second rule is to accept the burden of controlling Ẕ̶̨̫̹̌͊͌͑͊̕͢͟a̡̜̦̝͓͇͗̉̆̂͋̏͗̍ͅl̡̛̝͍̅͆̎̊̇̕͜͢ģ̧̧͍͓̜̲͖̹̂͋̆̃̑͗̋͌̊̏ͅǫ̷̧͓̣͚̞̣̋̂̑̊̂̀̿̀̚͟͠ͅ by ensuring he does not escape your API boundary.
-
- With that out of the way… this package lets you write code that can run both
- synchronously and asynchronously. This is useful if you have fairly complex
- logic for which you don't want to write multiple implementations. See
- [`package-hash`](https://github.com/novemberborn/package-hash) for instance.
-
- This is best shown by example. Let's say you have a `hashFile()` function:
-
- ```js
- const crypto = require('crypto')
- const fs = require('fs')
-
- function hashFile (file) {
- return new Promise((resolve, reject) => {
- fs.readFile(file, (err, buffer) => err ? reject(err) : resolve(buffer))
- })
- .then(buffer => {
- const hash = crypto.createHash('sha1')
- hash.update(buffer)
- return hash.digest('hex')
- })
- }
- ```
-
- A synchronous version could be implemented like this:
-
- ```js
- function hashFileSync (file) {
- const buffer = fs.readFileSync(file)
- const hash = crypto.createHash('sha1')
- hash.update(buffer)
- return hash.digest('hex')
- }
- ```
-
- Here's the version that uses `release-zalgo`:
-
- ```js
- const crypto = require('crypto')
- const fs = require('fs')
-
- const releaseZalgo = require('release-zalgo')
-
- const readFile = {
- async (file) {
- return new Promise((resolve, reject) => {
- fs.readFile(file, (err, buffer) => err ? reject(err) : resolve(buffer))
- })
- },
-
- sync (file) {
- return fs.readFileSync(file)
- }
- }
-
- function run (zalgo, file) {
- return zalgo.run(readFile, file)
- .then(buffer => {
- const hash = crypto.createHash('sha1')
- hash.update(buffer)
- return hash.digest('hex')
- })
- }
-
- function hashFile (file) {
- return run(releaseZalgo.async(), file)
- }
-
- function hashFileSync (file) {
- const result = run(releaseZalgo.sync(), file)
- return releaseZalgo.unwrapSync(result)
- }
- ```
-
- Note how close the `run()` implementation is to the original `hashFile()`.
-
- Just don't do this:
-
- ```js
- function badExample (zalgo, file) {
- let buffer
- zalgo.run(readFile, file)
- .then(result => { buffer = result })
-
- const hash = crypto.createHash('sha1')
- hash.update(buffer)
- return hash.digest('hex')
- }
- ```
-
- This won't work asynchronously. Just pretend you're working with promises and
- you'll be OK.
-
- ## API
-
- First require the package:
-
- ```js
- const releaseZalgo = require('release-zalgo')
- ```
-
- ### `releaseZalgo.sync()`
-
- Returns a `zalgo` object that runs code synchronously:
-
- ```js
- const zalgo = releaseZalgo.sync()
- ```
-
- ### `releaseZalgo.async()`
-
- Returns a `zalgo` object that runs code asynchronously:
-
- ```js
- const zalgo = releaseZalgo.async()
- ```
-
- ### `releaseZalgo.unwrapSync(thenable)`
-
- Synchronously unwraps a [thenable], which is returned when running
- synchronously. Returns the [thenable]s fulfilment value, or throws its
- rejection reason. Throws if the [thenable] is asynchronous.
-
- ### `zalgo.run(executors, ...args)`
-
- When running synchronously, `executors.sync()` is called. When running
- asynchronously `executors.async()` is used. The executer is invoked immediately
- and passed the remaining arguments.
-
- For asynchronous execution a `Promise` is returned. It is fulfilled with
- `executors.async()`'s return value, or rejected if `executors.async()` throws.
-
- For synchronous execution a *[thenable]* is returned. It has the same methods as
- `Promise` except that callbacks are invoked immediately. The [thenable] is
- fulfilled with `executors.sync()`'s return value, or rejected if
- `executors.sync()` throws.
-
- ### `zalgo.all(arr)`
-
- When running synchronously, returns a new [thenable] which is fulfilled with
- an array, after unwrapping all items in `arr`.
-
- When running asynchronously, delegates to `Promise.all(arr)`.
-
- ### `zalgo.returns(value)`
-
- When running synchronously, returns a new [thenable] which is fulfilled with
- `value`.
-
- When running asynchronously, delegates to `Promise.resolve(value)`.
-
- ### `zalgo.throws(reason)`
-
- When running synchronously, returns a new [thenable] which is rejected with
- `reason`.
-
- When running asynchronously, delegates to `Promise.reject(reason)`.
-
- ### Thenables
-
- Thenables are returned when running sychronously. They're much like `Promise`s,
- in that they have `then()` and `catch()` methods. You can pass callbacks and
- they'll be invoked with the fulfilment value or rejection reason. Callbacks
- can return other thenables or throw exceptions.
-
- Note that `then()` and `catch()` must be called on the thenable, e.g.
- `thenable.then()`, not `(thenable.then)()`.
-
- Thenables should not be exposed outside of your API. Use
- `releaseZalgo.unwrapSync()` to unwrap them.
-
- [thenable]: #thenables
|