ffb210fee9c7ddf9c901a24f137999f30fff997e.svn-base
5.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# 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