UNPKG

8.73 kBMarkdownView Raw
1# promise-toolbox [![Build Status](https://travis-ci.org/JsCommunity/promise-toolbox.png?branch=master)](https://travis-ci.org/JsCommunity/promise-toolbox)
2
3> Essential utils for promises.
4
5Features:
6
7- small (< 150 KB with all dependencies, < 5 KB with gzip)
8- nice with ES2015 / ES2016 syntax
9
10## Install
11
12Installation of the [npm package](https://npmjs.org/package/promise-toolbox):
13
14```
15> npm install --save promise-toolbox
16```
17
18## Usage
19
20If your environment may not natively support promises, you should use a polyfill such as [native-promise-only](https://github.com/getify/native-promise-only).
21
22On Node, if you want to use a specific promise implementation,
23[Bluebird](http://bluebirdjs.com/docs/why-bluebird.html) for instance
24to have better performance, you can override the global Promise
25variable:
26
27```js
28global.Promise = require('bluebird')
29```
30
31> Note that it should only be done at the application level, never in
32> a library!
33
34### Decorators
35
36#### cancellable
37
38> Make your async functions cancellable.
39
40```js
41import { cancellable } from 'promise-toolbox'
42
43const asyncFunction = cancellable(async function (cancellation, a, b) {
44 cancellation.catch(() => {
45 // do stuff regarding the cancellation request.
46 })
47
48 // do other stuff.
49})
50
51const promise = asyncFunction('foo', 'bar')
52promise.cancel()
53```
54
55If the function is a method of a class or an object, you can use
56`cancellable` as a decorator:
57
58```js
59class MyClass {
60 @cancellable
61 async asyncMethod (cancellation, a, b) {
62 cancellation.catch(() => {
63 // do stuff regarding the cancellation request.
64 })
65
66 // do other stuff.
67 }
68}
69```
70
71### Functions
72
73#### defer()
74
75> Discouraged but sometimes necessary way to create a promise.
76
77```js
78import { defer } from 'promise-toolbox'
79
80const { promise, resolve } = defer()
81
82promise.then(value => {
83 console.log(value)
84})
85
86resolve(3)
87```
88
89#### fromCallback(cb => fn(arg1, ..., argn, cb))
90
91> Easiest and most efficient way to promisify a function call.
92
93```js
94import { fromCallback } from 'promise-toolbox'
95
96fromCallback(cb => fs.readFile('foo.txt', cb))
97 .then(content => {
98 console.log(content)
99 })
100```
101
102#### isPromise(value)
103
104```js
105import { isPromise } from 'promise-toolbox'
106
107if (isPromise(foo())) {
108 console.log('foo() returns a promise')
109}
110```
111
112#### join(p1, ..., pn, cb) / join([p1, ..., pn], cb)
113
114> Easiest and most efficient way to wait for a fixed amount of
115> promises.
116
117```js
118import { join } from 'promise-toolbox'
119
120join(getPictures(), getComments(), getTweets(), (pictures, comments, tweets) => {
121 console.log(`in total: ${pictures.length + comments.length + tweets.length}`)
122})
123```
124
125### Pseudo-methods
126
127This function can be used as if they were methods, i.e. by passing the
128promise (or promises) as the context.
129
130This is extremely easy using [ES2016's bind syntax](https://github.com/zenparsing/es-function-bind).
131
132```js
133const promises = [
134 Promise.resolve('foo'),
135 Promise.resolve('bar')
136]
137
138promises::all().then(values => {
139 console.log(values)
140})
141// → [ 'foo', 'bar' ]
142```
143
144If you are still an older version of ECMAScript, fear not: simply pass
145the promise (or promises) as the first argument of the `.call()`
146method:
147
148```js
149var promises = [
150 Promise.resolve('foo'),
151 Promise.resolve('bar')
152]
153
154all.call(promises).then(function (values) {
155 console.log(values)
156})
157// → [ 'foo', 'bar' ]
158```
159
160#### promises::all([ mapper ])
161
162> Waits for all promises of a collection to be resolved.
163>
164> Contrary to the standard `Promise.all()`, this function works also
165> with objects.
166
167```js
168import { all } from 'promise-toolbox'
169
170[
171 Promise.resolve('foo'),
172 Promise.resolve('bar')
173]::all().then(value => {
174 console.log(value)
175 // → ['foo', 'bar']
176})
177
178{
179 foo: Promise.resolve('foo'),
180 bar: Promise.resolve('bar')
181}::all().then(value => {
182 console.log(value)
183 // → {
184 // foo: 'foo',
185 // bar: 'bar'
186 // }
187})
188```
189
190#### promise::asCallback(cb)
191
192> Register a node-style callback on this promise.
193
194```js
195import { asCallback } from 'promise-toolbox'
196
197// This function can be used either with node-style callbacks or with
198// promises.
199function getDataFor (input, callback) {
200 return dataFromDataBase(input)::asCallback(callback)
201}
202```
203
204#### promise::catchPlus(predicate, cb)
205
206> Similar to `Promise#catch()` but:
207>
208> - support predicates
209> - do not catch `ReferenceError`, `SyntaxError` or `TypeError` unless
210> they match a predicate because they are usually programmer errors
211> and should be handled separately.
212
213```js
214somePromise.then(() => {
215 return a.b.c.d()
216})::catchPlus(TypeError, ReferenceError, reason => {
217 // Will end up here on programmer error
218})::catchPlus(NetworkError, TimeoutError, reason => {
219 // Will end up here on expected everyday network errors
220})::catchPlus(reason => {
221 // Catch any unexpected errors
222})
223```
224
225#### promise::delay(ms)
226
227> Delays the resolution of a promise by `ms` milliseconds.
228>
229> Note: the rejection is not delayed.
230
231```js
232console.log(await Promise.resolve('500ms passed')::delay(500))
233// → 500 ms passed
234```
235
236Also works with a value:
237
238```js
239console.log(await delay.call('500ms passed', 500))
240// → 500 ms passed
241```
242
243#### promises::forEach(cb)
244
245> Iterates in order over a collection of promises waiting for each of
246> them to be resolved.
247
248```js
249[
250 Promise.resolve('foo'),
251 Promise.resolve('bar'),
252]::forEach(value => {
253 console.log(value)
254})
255// →
256// foo
257// bar
258```
259
260#### promise::lastly(cb)
261
262> Execute a handler regardless of the promise fate. Similar to the
263> `finally` block in synchronous codes.
264>
265> The resolution value or rejection reason of the initial promise is
266> forwarded unless the callback rejects.
267
268```js
269import { lastly } from 'promise-toolbox'
270
271function ajaxGetAsync (url) {
272 return new Promise((resolve, reject) => {
273 const xhr = new XMLHttpRequest
274 xhr.addEventListener('error', reject)
275 xhr.addEventListener('load', resolve)
276 xhr.open('GET', url)
277 xhr.send(null)
278 })::lastly(() => {
279 $('#ajax-loader-animation').hide()
280 })
281}
282```
283
284#### fn::promisify([ context ]) / obj::promisifyAll()
285
286> Creates async functions taking node-style callbacks, create new ones
287> returning promises.
288
289```js
290import fs from 'fs'
291import { promisify, promisifyAll } from 'promise-toolbox'
292
293// Promisify a single function.
294//
295// If possible, the function name is kept and the new length is set.
296const readFile = fs.readFile::promisify()
297
298// Or all functions (own or inherited) exposed on a object.
299const fsPromise = fs::promisifyAll()
300
301readFile(__filename).then(content => console.log(content))
302
303fsPromise.readFile(__filename).then(content => console.log(content))
304```
305
306#### promise::reflect()
307
308> Returns a promise which resolves to an objects which reflects the
309> resolution of this promise.
310
311```js
312import { reflect } from 'promise-toolbox'
313
314const inspection = await promise::reflect()
315
316if (inspection.isFulfilled()) {
317 console.log(inspection.value())
318} else {
319 console.error(inspection.reason())
320}
321```
322
323#### promises::some(count)
324
325> Waits for `count` promises in a collection to be resolved.
326
327```js
328import { some } from 'promise-toolbox'
329
330const [ first, seconds ] = await [
331 ping('ns1.example.org'),
332 ping('ns2.example.org'),
333 ping('ns3.example.org'),
334 ping('ns4.example.org')
335]::some(2)
336```
337
338#### promise::tap(onResolved, onRejected)
339
340> Like `.then()` but the original resolution/rejection is forwarded.
341>
342> Like `::lastly()`, if the callback rejects, it takes over the
343> original resolution/rejection.
344
345```js
346import { tap } from 'promise-toolbox'
347
348// Contrary to .then(), using ::tap() does not change the resolution
349// value.
350const promise1 = Promise.resolve(42)::tap(value => {
351 console.log(value)
352})
353
354// Like .then, the second param is used in case of rejection.
355const promise2 = Promise.reject(42)::tap(null, reason => {
356 console.error(reason)
357})
358```
359
360#### promise::timeout(ms, [cb])
361
362> Call a callback if the promise is still pending after `ms`
363> milliseconds. Its resolution/rejection is forwarded.
364>
365> If the callback is omitted, the returned promise is rejected with a
366> `Timeout` error.
367
368```js
369import { timeout } from 'promise-toolbox'
370
371await doLongOperation()::timeout(100, () => {
372 return doFallbackOperation()
373})
374
375await doLongOperation()::timeout(100)
376```
377
378## Development
379
380### Installing dependencies
381
382```
383> npm install
384```
385
386### Compilation
387
388The sources files are watched and automatically recompiled on changes.
389
390```
391> npm run dev
392```
393
394### Tests
395
396```
397> npm run test-dev
398```
399
400## Contributions
401
402Contributions are *very* welcomed, either on the documentation or on
403the code.
404
405You may:
406
407- report any [issue](https://github.com/JsCommunity/promise-toolbox/issues)
408 you've encountered;
409- fork and create a pull request.
410
411## License
412
413ISC © [Julien Fontanet](https://github.com/julien-f)