UNPKG

8.72 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#### promisify(fn, [ context ]) / promisifyAll(obj)
126
127> Creates async functions taking node-style callbacks, create new ones
128> returning promises.
129
130```js
131import fs from 'fs'
132import { promisify, promisifyAll } from 'promise-toolbox'
133
134// Promisify a single function.
135//
136// If possible, the function name is kept and the new length is set.
137const readFile = promisify(fs.readFile)
138
139// Or all functions (own or inherited) exposed on a object.
140const fsPromise = promisifyAll(fs)
141
142readFile(__filename).then(content => console.log(content))
143
144fsPromise.readFile(__filename).then(content => console.log(content))
145```
146
147### Pseudo-methods
148
149This function can be used as if they were methods, i.e. by passing the
150promise (or promises) as the context.
151
152This is extremely easy using [ES2016's bind syntax](https://github.com/zenparsing/es-function-bind).
153
154```js
155const promises = [
156 Promise.resolve('foo'),
157 Promise.resolve('bar')
158]
159
160promises::all().then(values => {
161 console.log(values)
162})
163// → [ 'foo', 'bar' ]
164```
165
166If you are still an older version of ECMAScript, fear not: simply pass
167the promise (or promises) as the first argument of the `.call()`
168method:
169
170```js
171var promises = [
172 Promise.resolve('foo'),
173 Promise.resolve('bar')
174]
175
176all.call(promises).then(function (values) {
177 console.log(values)
178})
179// → [ 'foo', 'bar' ]
180```
181
182#### promises::all([ mapper ])
183
184> Waits for all promises of a collection to be resolved.
185>
186> Contrary to the standard `Promise.all()`, this function works also
187> with objects.
188
189```js
190import { all } from 'promise-toolbox'
191
192[
193 Promise.resolve('foo'),
194 Promise.resolve('bar')
195]::all().then(value => {
196 console.log(value)
197 // → ['foo', 'bar']
198})
199
200{
201 foo: Promise.resolve('foo'),
202 bar: Promise.resolve('bar')
203}::all().then(value => {
204 console.log(value)
205 // → {
206 // foo: 'foo',
207 // bar: 'bar'
208 // }
209})
210```
211
212#### promise::asCallback(cb)
213
214> Register a node-style callback on this promise.
215
216```js
217import { asCallback } from 'promise-toolbox'
218
219// This function can be used either with node-style callbacks or with
220// promises.
221function getDataFor (input, callback) {
222 return dataFromDataBase(input)::asCallback(callback)
223}
224```
225
226#### promise::catchPlus(predicate, cb)
227
228> Similar to `Promise#catch()` but:
229>
230> - support predicates
231> - do not catch `ReferenceError`, `SyntaxError` or `TypeError` unless
232> they match a predicate because they are usually programmer errors
233> and should be handled separately.
234
235```js
236somePromise.then(() => {
237 return a.b.c.d()
238})::catchPlus(TypeError, ReferenceError, reason => {
239 // Will end up here on programmer error
240})::catchPlus(NetworkError, TimeoutError, reason => {
241 // Will end up here on expected everyday network errors
242})::catchPlus(reason => {
243 // Catch any unexpected errors
244})
245```
246
247#### promise::delay(ms)
248
249> Delays the resolution of a promise by `ms` milliseconds.
250>
251> Note: the rejection is not delayed.
252
253```js
254console.log(await Promise.resolve('500ms passed')::delay(500))
255// → 500 ms passed
256```
257
258Also works with a value:
259
260```js
261console.log(await delay.call('500ms passed', 500))
262// → 500 ms passed
263```
264
265#### promises::forEach(cb)
266
267> Iterates in order over a collection of promises waiting for each of
268> them to be resolved.
269
270```js
271[
272 Promise.resolve('foo'),
273 Promise.resolve('bar'),
274]::forEach(value => {
275 console.log(value)
276})
277// →
278// foo
279// bar
280```
281
282#### promise::lastly(cb)
283
284> Execute a handler regardless of the promise fate. Similar to the
285> `finally` block in synchronous codes.
286>
287> The resolution value or rejection reason of the initial promise is
288> forwarded unless the callback rejects.
289
290```js
291import { lastly } from 'promise-toolbox'
292
293function ajaxGetAsync (url) {
294 return new Promise((resolve, reject) => {
295 const xhr = new XMLHttpRequest
296 xhr.addEventListener('error', reject)
297 xhr.addEventListener('load', resolve)
298 xhr.open('GET', url)
299 xhr.send(null)
300 })::lastly(() => {
301 $('#ajax-loader-animation').hide()
302 })
303}
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)