1 | <a href="http://promisesaplus.com/">
|
2 | <img src="http://promisesaplus.com/assets/logo-small.png" alt="Promises/A+ logo"
|
3 | title="Promises/A+ 1.1 compliant" align="right" />
|
4 | </a>
|
5 |
|
6 | # Overview
|
7 |
|
8 | Yaku is full compatible with ES6's native [Promise][native], but much faster, and more error friendly.
|
9 | If you want to learn how Promise works, read the minimum implementation [yaku.aplus][]. Without comments, it is only 80 lines of code (gzipped size is 0.5KB).
|
10 | It only implements the `constructor` and `then`.
|
11 |
|
12 | Yaku passed all the tests of [promises-aplus-tests][], [promises-es6-tests][], and even the [core-js tests][].
|
13 |
|
14 | I am not an optimization freak, I try to keep the source code readable and maintainable.
|
15 | I write this lib to research one of my data structure ideas: [docs/lazyTree.md][].
|
16 |
|
17 | [![NPM version](https://badge.fury.io/js/yaku.svg)](http://badge.fury.io/js/yaku) [![Build Status](https://travis-ci.org/ysmood/yaku.svg)](https://travis-ci.org/ysmood/yaku) [![Deps Up to Date](https://david-dm.org/ysmood/yaku.svg?style=flat)](https://david-dm.org/ysmood/yaku) [![Coverage Status](https://coveralls.io/repos/ysmood/yaku/badge.svg?branch=master&service=github)](https://coveralls.io/github/ysmood/yaku?branch=master)
|
18 |
|
19 |
|
20 |
|
21 | # Features
|
22 |
|
23 | - The best for mobile, gzipped file is only 1.9KB
|
24 | - Supports "uncaught rejection" and "long stack trace", [Comparison][docs/debugHelperComparison.md]
|
25 | - Works on IE5+ and other major browsers
|
26 | - 100% statement and branch test coverage
|
27 | - Better CPU and memory performance than the native Promise
|
28 | - Well commented source code with every Promises/A+ spec
|
29 | - Highly modularized extra helpers, no pollution to its pure ES6 implements
|
30 | - Supports ES7 `finally`
|
31 |
|
32 |
|
33 |
|
34 | # Quick Start
|
35 |
|
36 | ## Node.js
|
37 |
|
38 | ```shell
|
39 | npm install yaku
|
40 | ```
|
41 |
|
42 | Then:
|
43 |
|
44 | ```js
|
45 | var Promise = require('yaku');
|
46 | ```
|
47 |
|
48 | Or if you don't want any extra debug helper, ES6 only version is here:
|
49 |
|
50 | ```js
|
51 | var Promise = require('yaku/lib/yaku.core');
|
52 | ```
|
53 |
|
54 | Or if you only want aplus support:
|
55 |
|
56 | ```js
|
57 | var Promise = require('yaku/lib/yaku.aplus');
|
58 | ```
|
59 |
|
60 | ## Browser
|
61 |
|
62 | Use something like [Browserify][] or [Webpack][], or download the `yaku.js` file from [release page][].
|
63 | Raw usage without:
|
64 |
|
65 | ```html
|
66 | <script type="text/javascript" src ="yaku.js"></script>
|
67 | <script>
|
68 | // Yaku will be assigned to `window.Yaku`.
|
69 | var Promise = Yaku;
|
70 | </script>
|
71 | ```
|
72 |
|
73 |
|
74 |
|
75 | # Change Log
|
76 |
|
77 | [docs/changelog.md](docs/changelog.md)
|
78 |
|
79 |
|
80 |
|
81 | # Compare to Other Promise Libs
|
82 |
|
83 | These comparisons only reflect some limited truth, no one is better than all others on all aspects.
|
84 | There are tons of Promises/A+ implementations, you can see them [here](https://promisesaplus.com/implementations). Only some of the famous ones were tested.
|
85 |
|
86 | ```
|
87 | Date: Sat Dec 17 2016 22:15:40 GMT+0800 (CST)
|
88 | Node v7.2.1
|
89 | OS darwin
|
90 | Arch x64
|
91 | CPU Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz
|
92 | ```
|
93 |
|
94 | | name | unit tests | coverage | 1ms async task | optional helpers | helpers | gzip |
|
95 | | ---- | ---------- | -------- | -------------- | ---------------- | ------- | ---- |
|
96 | | [yaku][]@0.17.4 | ✓ | 100% 100% | 221ms / 108MB | ✓ | 34 | 1.9KB |
|
97 | | [yaku.core][]@0.17.4 | ✓ | 100% 100% | 217ms / 108MB | ✓ | 28 | 1.6KB |
|
98 | | [yaku.aplus][]@0.17.4 | x (90 failed) | 100% 100% | 262ms / 116MB | ✓ | 7 | 0.5KB |
|
99 | | [bluebird][]@3.4.6 | x (34 failed) | 99% 96% | 207ms / 81MB | partial | 102 | 15.9KB |
|
100 | | [es6-promise][]@4.0.5 | x (52 failed) | ? ? | 432ms / 114MB | x | 12 | 2.4KB |
|
101 | | [pinkie][]@2.0.4 | x (44 failed) | ? ? | 313ms / 135MB | ✓ | 10 | 1.2KB |
|
102 | | [native][]@7.2.1 | ✓ | ? ? | 376ms / 134MB | x | 10 | 0KB |
|
103 | | [core-js][]@2.4.1 | x (9 failed) | ? ? | 394ms / 142MB | x | 10 | 5KB |
|
104 | | [es6-shim][]@0.35.2 | ✓ | ? ? | 390ms / 136MB | x | 10 | 15.5KB |
|
105 | | [q][]@1.4.1 | x (42 failed) | ? ? | 1432ms / 370MB | x | 74 | 4.6KB |
|
106 | | [my-promise][]@1.1.0 | x (10 failed) | ? ? | 786ms / 232MB | x | 10 | 3.9KB |
|
107 |
|
108 | - **unit test**: [promises-aplus-tests][], [promises-es6-tests][], and even the [core-js tests][].
|
109 |
|
110 | - **coverage**: statement coverage and branch coverage.
|
111 |
|
112 | - **helpers**: extra methods that help with your promise programming, such as
|
113 | async flow control helpers, debug helpers. For more details: [docs/debugHelperComparison.md][].
|
114 |
|
115 | - **1ms async task**: `npm run no -- benchmark`, the smaller the better (total time / memory rss).
|
116 |
|
117 | - **promises-es6-tests**: If you want to test `bluebird` against promises-es6-tests,
|
118 | run `npm run no -- test-es6 --shim bluebird`.
|
119 |
|
120 | - **optional helpers**: Whether the helpers can be imported separately or not,
|
121 | which means you can load the lib without helpers. Such as the `bluebird-core`, it will inevitably load
|
122 | some nonstandard helpers: `spread`, `promisify`, etc.
|
123 |
|
124 |
|
125 | # FAQ
|
126 |
|
127 | - `catch` on old browsers (IE7, IE8 etc)?
|
128 |
|
129 | > In ECMA-262 spec, `catch` cannot be used as method name. You have to alias the method name or use something like `Promise.resolve()['catch'](function() {})` or `Promise.resolve().then(null, function() {})`.
|
130 |
|
131 | - When using with Babel and Regenerator, the unhandled rejection doesn't work.
|
132 |
|
133 | > Because Regenerator use global Promise directly and don't have an api to set the Promise lib.
|
134 | > You have to import Yaku globally to make it use Yaku: `require("yaku/lib/global");`.
|
135 |
|
136 | - The name Yaku is weird?
|
137 |
|
138 | > The name `yaku` comes from the word `約束(yaku soku)` which means promise.
|
139 |
|
140 |
|
141 | # Unhandled Rejection
|
142 |
|
143 | Yaku will report any unhandled rejection via `console.error` by default, in case you forget to write `catch`.
|
144 | You can catch them manually:
|
145 |
|
146 | - Browser: `window.onunhandledrejection = ({ promise, reason }) => { /* Your Code */ };`
|
147 | - Node: `process.on("unhandledRejection", (reason, promise) => { /* Your Code */ });`
|
148 |
|
149 | For more spec read [Unhandled Rejection Tracking Browser Events](https://github.com/domenic/unhandled-rejections-browser-spec).
|
150 |
|
151 |
|
152 | # API
|
153 |
|
154 | - #### require('yaku')
|
155 | - [Yaku(executor)](#yakuexecutor)
|
156 | - [then(onFulfilled, onRejected)](#thenonfulfilled-onrejected)
|
157 | - [catch(onRejected)](#catchonrejected)
|
158 | - [finally(onFinally)](#finallyonfinally)
|
159 | - [Yaku.resolve(value)](#yakuresolvevalue)
|
160 | - [Yaku.reject(reason)](#yakurejectreason)
|
161 | - [Yaku.race(iterable)](#yakuraceiterable)
|
162 | - [Yaku.all(iterable)](#yakualliterable)
|
163 | - [Yaku.Symbol](#yakusymbol)
|
164 | - [Yaku.speciesConstructor(O, defaultConstructor)](#yakuspeciesconstructoro-defaultconstructor)
|
165 | - [Yaku.unhandledRejection(reason, p)](#yakuunhandledrejectionreason-p)
|
166 | - [Yaku.rejectionHandled(reason, p)](#yakurejectionhandledreason-p)
|
167 | - [Yaku.enableLongStackTrace](#yakuenablelongstacktrace)
|
168 | - [Yaku.nextTick](#yakunexttick)
|
169 |
|
170 | - #### require('yaku/lib/utils')
|
171 | - [all(limit, list)](#alllimit-list)
|
172 | - [any(iterable)](#anyiterable)
|
173 | - [async(gen)](#asyncgen)
|
174 | - [callbackify(fn, self)](#callbackifyfn-self)
|
175 | - [Deferred](#deferred)
|
176 | - [flow(list)](#flowlist)
|
177 | - [guard(type, onRejected)](#guardtype-onrejected)
|
178 | - [if(cond, trueFn, falseFn)](#ifcond-truefn-falsefn)
|
179 | - [isPromise(obj)](#ispromiseobj)
|
180 | - [never()](#never)
|
181 | - [promisify(fn, self)](#promisifyfn-self)
|
182 | - [sleep(time, val)](#sleeptime-val)
|
183 | - [Observable](#observable)
|
184 | - [retry(countdown, span, fn, this)](#retrycountdown-span-fn-this)
|
185 | - [throw(err)](#throwerr)
|
186 | - [timeout(promise, time, reason)](#timeoutpromise-time-reason)
|
187 |
|
188 | - #### require('yaku/lib/Observable')
|
189 | - [Observable(executor)](#observableexecutor)
|
190 | - [next(value)](#nextvalue)
|
191 | - [error(value)](#errorvalue)
|
192 | - [publisher](#publisher)
|
193 | - [subscribers](#subscribers)
|
194 | - [subscribe(onNext, onError)](#subscribeonnext-onerror)
|
195 | - [unsubscribe](#unsubscribe)
|
196 | - [Observable.merge(iterable)](#observablemergeiterable)
|
197 |
|
198 | ---------------------------------------
|
199 |
|
200 |
|
201 | - ### **[Yaku(executor)](src/yaku.js?source#L49)**
|
202 |
|
203 | This class follows the [Promises/A+](https://promisesaplus.com) and
|
204 | [ES6](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-promise-objects) spec
|
205 | with some extra helpers.
|
206 |
|
207 | - **<u>param</u>**: `executor` { _Function_ }
|
208 |
|
209 | Function object with two arguments resolve, reject.
|
210 | The first argument fulfills the promise, the second argument rejects it.
|
211 | We can call these functions, once our operation is completed.
|
212 |
|
213 | - ### **[then(onFulfilled, onRejected)](src/yaku.js?source#L97)**
|
214 |
|
215 | Appends fulfillment and rejection handlers to the promise,
|
216 | and returns a new promise resolving to the return value of the called handler.
|
217 |
|
218 | - **<u>param</u>**: `onFulfilled` { _Function_ }
|
219 |
|
220 | Optional. Called when the Promise is resolved.
|
221 |
|
222 | - **<u>param</u>**: `onRejected` { _Function_ }
|
223 |
|
224 | Optional. Called when the Promise is rejected.
|
225 |
|
226 | - **<u>return</u>**: { _Yaku_ }
|
227 |
|
228 | It will return a new Yaku which will resolve or reject after
|
229 |
|
230 | - **<u>example</u>**:
|
231 |
|
232 | the current Promise.
|
233 | ```js
|
234 | var Promise = require('yaku');
|
235 | var p = Promise.resolve(10);
|
236 |
|
237 | p.then((v) => {
|
238 | console.log(v);
|
239 | });
|
240 | ```
|
241 |
|
242 | - ### **[catch(onRejected)](src/yaku.js?source#L124)**
|
243 |
|
244 | The `catch()` method returns a Promise and deals with rejected cases only.
|
245 | It behaves the same as calling `Promise.prototype.then(undefined, onRejected)`.
|
246 |
|
247 | - **<u>param</u>**: `onRejected` { _Function_ }
|
248 |
|
249 | A Function called when the Promise is rejected.
|
250 | This function has one argument, the rejection reason.
|
251 |
|
252 | - **<u>return</u>**: { _Yaku_ }
|
253 |
|
254 | A Promise that deals with rejected cases only.
|
255 |
|
256 | - **<u>example</u>**:
|
257 |
|
258 | ```js
|
259 | var Promise = require('yaku');
|
260 | var p = Promise.reject(new Error("ERR"));
|
261 |
|
262 | p['catch']((v) => {
|
263 | console.log(v);
|
264 | });
|
265 | ```
|
266 |
|
267 | - ### **[finally(onFinally)](src/yaku.js?source#L144)**
|
268 |
|
269 | Register a callback to be invoked when a promise is settled (either fulfilled or rejected).
|
270 | Similar with the try-catch-finally, it's often used for cleanup.
|
271 |
|
272 | - **<u>param</u>**: `onFinally` { _Function_ }
|
273 |
|
274 | A Function called when the Promise is settled.
|
275 | It will not receive any argument.
|
276 |
|
277 | - **<u>return</u>**: { _Yaku_ }
|
278 |
|
279 | A Promise that will reject if onFinally throws an error or returns a rejected promise.
|
280 | Else it will resolve previous promise's final state (either fulfilled or rejected).
|
281 |
|
282 | - **<u>example</u>**:
|
283 |
|
284 | ```js
|
285 | var Promise = require('yaku');
|
286 | var p = Math.random() > 0.5 ? Promise.resolve() : Promise.reject();
|
287 | p.finally(() => {
|
288 | console.log('finally');
|
289 | });
|
290 | ```
|
291 |
|
292 | - ### **[Yaku.resolve(value)](src/yaku.js?source#L174)**
|
293 |
|
294 | The `Promise.resolve(value)` method returns a Promise object that is resolved with the given value.
|
295 | If the value is a thenable (i.e. has a then method), the returned promise will "follow" that thenable,
|
296 | adopting its eventual state; otherwise the returned promise will be fulfilled with the value.
|
297 |
|
298 | - **<u>param</u>**: `value` { _Any_ }
|
299 |
|
300 | Argument to be resolved by this Promise.
|
301 | Can also be a Promise or a thenable to resolve.
|
302 |
|
303 | - **<u>return</u>**: { _Yaku_ }
|
304 |
|
305 | - **<u>example</u>**:
|
306 |
|
307 | ```js
|
308 | var Promise = require('yaku');
|
309 | var p = Promise.resolve(10);
|
310 | ```
|
311 |
|
312 | - ### **[Yaku.reject(reason)](src/yaku.js?source#L188)**
|
313 |
|
314 | The `Promise.reject(reason)` method returns a Promise object that is rejected with the given reason.
|
315 |
|
316 | - **<u>param</u>**: `reason` { _Any_ }
|
317 |
|
318 | Reason why this Promise rejected.
|
319 |
|
320 | - **<u>return</u>**: { _Yaku_ }
|
321 |
|
322 | - **<u>example</u>**:
|
323 |
|
324 | ```js
|
325 | var Promise = require('yaku');
|
326 | var p = Promise.reject(new Error("ERR"));
|
327 | ```
|
328 |
|
329 | - ### **[Yaku.race(iterable)](src/yaku.js?source#L212)**
|
330 |
|
331 | The `Promise.race(iterable)` method returns a promise that resolves or rejects
|
332 | as soon as one of the promises in the iterable resolves or rejects,
|
333 | with the value or reason from that promise.
|
334 |
|
335 | - **<u>param</u>**: `iterable` { _iterable_ }
|
336 |
|
337 | An iterable object, such as an Array.
|
338 |
|
339 | - **<u>return</u>**: { _Yaku_ }
|
340 |
|
341 | The race function returns a Promise that is settled
|
342 | the same way as the first passed promise to settle.
|
343 | It resolves or rejects, whichever happens first.
|
344 |
|
345 | - **<u>example</u>**:
|
346 |
|
347 | ```js
|
348 | var Promise = require('yaku');
|
349 | Promise.race([
|
350 | 123,
|
351 | Promise.resolve(0)
|
352 | ])
|
353 | .then((value) => {
|
354 | console.log(value); // => 123
|
355 | });
|
356 | ```
|
357 |
|
358 | - ### **[Yaku.all(iterable)](src/yaku.js?source#L268)**
|
359 |
|
360 | The `Promise.all(iterable)` method returns a promise that resolves when
|
361 | all of the promises in the iterable argument have resolved.
|
362 |
|
363 | The result is passed as an array of values from all the promises.
|
364 | If something passed in the iterable array is not a promise,
|
365 | it's converted to one by Promise.resolve. If any of the passed in promises rejects,
|
366 | the all Promise immediately rejects with the value of the promise that rejected,
|
367 | discarding all the other promises whether or not they have resolved.
|
368 |
|
369 | - **<u>param</u>**: `iterable` { _iterable_ }
|
370 |
|
371 | An iterable object, such as an Array.
|
372 |
|
373 | - **<u>return</u>**: { _Yaku_ }
|
374 |
|
375 | - **<u>example</u>**:
|
376 |
|
377 | ```js
|
378 | var Promise = require('yaku');
|
379 | Promise.all([
|
380 | 123,
|
381 | Promise.resolve(0)
|
382 | ])
|
383 | .then((values) => {
|
384 | console.log(values); // => [123, 0]
|
385 | });
|
386 | ```
|
387 |
|
388 | - **<u>example</u>**:
|
389 |
|
390 | Use with iterable.
|
391 | ```js
|
392 | var Promise = require('yaku');
|
393 | Promise.all((function * () {
|
394 | yield 10;
|
395 | yield new Promise(function (r) { setTimeout(r, 1000, "OK") });
|
396 | })())
|
397 | .then((values) => {
|
398 | console.log(values); // => [123, 0]
|
399 | });
|
400 | ```
|
401 |
|
402 | - ### **[Yaku.Symbol](src/yaku.js?source#L304)**
|
403 |
|
404 | The ES6 Symbol object that Yaku should use, by default it will use the
|
405 | global one.
|
406 |
|
407 | - **<u>type</u>**: { _Object_ }
|
408 |
|
409 | - **<u>example</u>**:
|
410 |
|
411 | ```js
|
412 | var core = require("core-js/library");
|
413 | var Promise = require("yaku");
|
414 | Promise.Symbol = core.Symbol;
|
415 | ```
|
416 |
|
417 | - ### **[Yaku.speciesConstructor(O, defaultConstructor)](src/yaku.js?source#L319)**
|
418 |
|
419 | Use this api to custom the species behavior.
|
420 | https://tc39.github.io/ecma262/#sec-speciesconstructor
|
421 |
|
422 | - **<u>param</u>**: `O` { _Any_ }
|
423 |
|
424 | The current this object.
|
425 |
|
426 | - **<u>param</u>**: `defaultConstructor` { _Function_ }
|
427 |
|
428 | - ### **[Yaku.unhandledRejection(reason, p)](src/yaku.js?source#L345)**
|
429 |
|
430 | Catch all possibly unhandled rejections. If you want to use specific
|
431 | format to display the error stack, overwrite it.
|
432 | If it is set, auto `console.error` unhandled rejection will be disabled.
|
433 |
|
434 | - **<u>param</u>**: `reason` { _Any_ }
|
435 |
|
436 | The rejection reason.
|
437 |
|
438 | - **<u>param</u>**: `p` { _Yaku_ }
|
439 |
|
440 | The promise that was rejected.
|
441 |
|
442 | - **<u>example</u>**:
|
443 |
|
444 | ```js
|
445 | var Promise = require('yaku');
|
446 | Promise.unhandledRejection = (reason) => {
|
447 | console.error(reason);
|
448 | };
|
449 |
|
450 | // The console will log an unhandled rejection error message.
|
451 | Promise.reject('my reason');
|
452 |
|
453 | // The below won't log the unhandled rejection error message.
|
454 | Promise.reject('v').catch(() => {});
|
455 | ```
|
456 |
|
457 | - ### **[Yaku.rejectionHandled(reason, p)](src/yaku.js?source#L360)**
|
458 |
|
459 | Emitted whenever a Promise was rejected and an error handler was
|
460 | attached to it (for example with `.catch()`) later than after an event loop turn.
|
461 |
|
462 | - **<u>param</u>**: `reason` { _Any_ }
|
463 |
|
464 | The rejection reason.
|
465 |
|
466 | - **<u>param</u>**: `p` { _Yaku_ }
|
467 |
|
468 | The promise that was rejected.
|
469 |
|
470 | - ### **[Yaku.enableLongStackTrace](src/yaku.js?source#L378)**
|
471 |
|
472 | It is used to enable the long stack trace.
|
473 | Once it is enabled, it can't be reverted.
|
474 | While it is very helpful in development and testing environments,
|
475 | it is not recommended to use it in production. It will slow down
|
476 | application and eat up memory.
|
477 | It will add an extra property `longStack` to the Error object.
|
478 |
|
479 | - **<u>example</u>**:
|
480 |
|
481 | ```js
|
482 | var Promise = require('yaku');
|
483 | Promise.enableLongStackTrace();
|
484 | Promise.reject(new Error("err")).catch((err) => {
|
485 | console.log(err.longStack);
|
486 | });
|
487 | ```
|
488 |
|
489 | - ### **[Yaku.nextTick](src/yaku.js?source#L401)**
|
490 |
|
491 | Only Node has `process.nextTick` function. For browser there are
|
492 | so many ways to polyfill it. Yaku won't do it for you, instead you
|
493 | can choose what you prefer. For example, this project
|
494 | [next-tick](https://github.com/medikoo/next-tick).
|
495 | By default, Yaku will use `process.nextTick` on Node, `setTimeout` on browser.
|
496 |
|
497 | - **<u>type</u>**: { _Function_ }
|
498 |
|
499 | - **<u>example</u>**:
|
500 |
|
501 | ```js
|
502 | var Promise = require('yaku');
|
503 | Promise.nextTick = require('next-tick');
|
504 | ```
|
505 |
|
506 | - **<u>example</u>**:
|
507 |
|
508 | You can even use sync resolution if you really know what you are doing.
|
509 | ```js
|
510 | var Promise = require('yaku');
|
511 | Promise.nextTick = fn => fn();
|
512 | ```
|
513 |
|
514 |
|
515 |
|
516 |
|
517 |
|
518 | # Utils
|
519 |
|
520 | It's a bundle of all the following functions. You can require them all with `var yutils = require("yaku/lib/utils")`,
|
521 | or require them separately like `require("yaku/lib/flow")`. If you want to use it in the browser, you have to use `browserify` or `webpack`. You can even use another Promise lib, such as:
|
522 |
|
523 | ```js
|
524 | require("yaku/lib/_").Promise = require("bluebird");
|
525 | var source = require("yaku/lib/source");
|
526 |
|
527 | // now "source" use bluebird instead of yaku.
|
528 | ```
|
529 |
|
530 | - ### **[all(limit, list)](src/utils.js?source#L46)**
|
531 |
|
532 | A function that helps run functions under a concurrent limitation.
|
533 | To run functions sequentially, use `yaku/lib/flow`.
|
534 |
|
535 | - **<u>param</u>**: `limit` { _Int_ }
|
536 |
|
537 | The max task to run at a time. It's optional.
|
538 | Default is `Infinity`.
|
539 |
|
540 | - **<u>param</u>**: `list` { _Iterable_ }
|
541 |
|
542 | Any [iterable](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) object. It should be a lazy iteralbe object,
|
543 | don't pass in a normal Array with promises.
|
544 |
|
545 | - **<u>return</u>**: { _Promise_ }
|
546 |
|
547 | - **<u>example</u>**:
|
548 |
|
549 | ```js
|
550 | var kit = require('nokit');
|
551 | var all = require('yaku/lib/all');
|
552 |
|
553 | var urls = [
|
554 | 'http://a.com',
|
555 | 'http://b.com',
|
556 | 'http://c.com',
|
557 | 'http://d.com'
|
558 | ];
|
559 | var tasks = function * () {
|
560 | var i = 0;
|
561 | yield kit.request(url[i++]);
|
562 | yield kit.request(url[i++]);
|
563 | yield kit.request(url[i++]);
|
564 | yield kit.request(url[i++]);
|
565 | }();
|
566 |
|
567 | all(tasks).then(() => kit.log('all done!'));
|
568 |
|
569 | all(2, tasks).then(() => kit.log('max concurrent limit is 2'));
|
570 |
|
571 | all(3, { next: () => {
|
572 | var url = urls.pop();
|
573 | return {
|
574 | done: !url,
|
575 | value: url && kit.request(url)
|
576 | };
|
577 | } })
|
578 | .then(() => kit.log('all done!'));
|
579 | ```
|
580 |
|
581 | - ### **[any(iterable)](src/utils.js?source#L65)**
|
582 |
|
583 | Similar with the `Promise.race`, but only rejects when every entry rejects.
|
584 |
|
585 | - **<u>param</u>**: `iterable` { _iterable_ }
|
586 |
|
587 | An iterable object, such as an Array.
|
588 |
|
589 | - **<u>return</u>**: { _Yaku_ }
|
590 |
|
591 | - **<u>example</u>**:
|
592 |
|
593 | ```js
|
594 | var any = require('yaku/lib/any');
|
595 | any([
|
596 | 123,
|
597 | Promise.resolve(0),
|
598 | Promise.reject(new Error("ERR"))
|
599 | ])
|
600 | .then((value) => {
|
601 | console.log(value); // => 123
|
602 | });
|
603 | ```
|
604 |
|
605 | - ### **[async(gen)](src/utils.js?source#L85)**
|
606 |
|
607 | Generator based async/await wrapper.
|
608 |
|
609 | - **<u>param</u>**: `gen` { _Generator_ }
|
610 |
|
611 | A generator function
|
612 |
|
613 | - **<u>return</u>**: { _Yaku_ }
|
614 |
|
615 | - **<u>example</u>**:
|
616 |
|
617 | ```js
|
618 | var async = require('yaku/lib/async');
|
619 | var sleep = require('yaku/lib/sleep');
|
620 |
|
621 | var fn = async(function * () {
|
622 | return yield sleep(1000, 'ok');
|
623 | });
|
624 |
|
625 | fn().then(function (v) {
|
626 | console.log(v);
|
627 | });
|
628 | ```
|
629 |
|
630 | - ### **[callbackify(fn, self)](src/utils.js?source#L94)**
|
631 |
|
632 | If a function returns promise, convert it to
|
633 | node callback style function.
|
634 |
|
635 | - **<u>param</u>**: `fn` { _Function_ }
|
636 |
|
637 | - **<u>param</u>**: `self` { _Any_ }
|
638 |
|
639 | The `this` to bind to the fn.
|
640 |
|
641 | - **<u>return</u>**: { _Function_ }
|
642 |
|
643 | - ### **[Deferred](src/utils.js?source#L100)**
|
644 |
|
645 | **deprecate** Create a `jQuery.Deferred` like object.
|
646 | It will cause some buggy problems, please don't use it.
|
647 |
|
648 | - ### **[flow(list)](src/utils.js?source#L158)**
|
649 |
|
650 | Creates a function that is the composition of the provided functions.
|
651 | See `yaku/lib/async`, if you need concurrent support.
|
652 |
|
653 | - **<u>param</u>**: `list` { _Iterable_ }
|
654 |
|
655 | Any [iterable](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) object. It should be a lazy iteralbe object,
|
656 | don't pass in a normal Array with promises.
|
657 |
|
658 | - **<u>return</u>**: { _Function_ }
|
659 |
|
660 | `(val) -> Promise` A function that will return a promise.
|
661 |
|
662 | - **<u>example</u>**:
|
663 |
|
664 | It helps to decouple sequential pipeline code logic.
|
665 | ```js
|
666 | var kit = require('nokit');
|
667 | var flow = require('yaku/lib/flow');
|
668 |
|
669 | function createUrl (name) {
|
670 | return "http://test.com/" + name;
|
671 | }
|
672 |
|
673 | function curl (url) {
|
674 | return kit.request(url).then((body) => {
|
675 | kit.log('get');
|
676 | return body;
|
677 | });
|
678 | }
|
679 |
|
680 | function save (str) {
|
681 | kit.outputFile('a.txt', str).then(() => {
|
682 | kit.log('saved');
|
683 | });
|
684 | }
|
685 |
|
686 | var download = flow(createUrl, curl, save);
|
687 | // same as "download = flow([createUrl, curl, save])"
|
688 |
|
689 | download('home');
|
690 | ```
|
691 |
|
692 | - **<u>example</u>**:
|
693 |
|
694 | Walk through first link of each page.
|
695 | ```js
|
696 | var kit = require('nokit');
|
697 | var flow = require('yaku/lib/flow');
|
698 |
|
699 | var list = [];
|
700 | function iter (url) {
|
701 | return {
|
702 | done: !url,
|
703 | value: url && kit.request(url).then((body) => {
|
704 | list.push(body);
|
705 | var m = body.match(/href="(.+?)"/);
|
706 | if (m) return m[0];
|
707 | });
|
708 | };
|
709 | }
|
710 |
|
711 | var walker = flow(iter);
|
712 | walker('test.com');
|
713 | ```
|
714 |
|
715 | - ### **[guard(type, onRejected)](src/utils.js?source#L187)**
|
716 |
|
717 | Enable a helper to catch specific error type.
|
718 | It will be directly attach to the prototype of the promise.
|
719 |
|
720 | - **<u>param</u>**: `type` { _class_ }
|
721 |
|
722 | - **<u>param</u>**: `onRejected` { _Function_ }
|
723 |
|
724 | - **<u>return</u>**: { _Promise_ }
|
725 |
|
726 | ```js
|
727 | var Promise = require('yaku');
|
728 | require('yaku/lib/guard');
|
729 |
|
730 | class AnError extends Error {
|
731 | }
|
732 |
|
733 | Promise.reject(new AnError('hey'))
|
734 | .guard(AnError, (err) => {
|
735 | // only log AnError type
|
736 | console.log(err);
|
737 | })
|
738 | .then(() => {
|
739 | console.log('done');
|
740 | })
|
741 | .guard(Error, (err) => {
|
742 | // log all error type
|
743 | console.log(err)
|
744 | });
|
745 | ```
|
746 |
|
747 | - ### **[if(cond, trueFn, falseFn)](src/utils.js?source#L207)**
|
748 |
|
749 | if-else helper
|
750 |
|
751 | - **<u>param</u>**: `cond` { _Promise_ }
|
752 |
|
753 | - **<u>param</u>**: `trueFn` { _Function_ }
|
754 |
|
755 | - **<u>param</u>**: `falseFn` { _Function_ }
|
756 |
|
757 | - **<u>return</u>**: { _Promise_ }
|
758 |
|
759 | - **<u>example</u>**:
|
760 |
|
761 | ```js
|
762 | var Promise = require('yaku');
|
763 | var yutils = require('yaku/lib/utils');
|
764 |
|
765 | yutils.if(Promise.resolve(false), () => {
|
766 | // true
|
767 | }, () => {
|
768 | // false
|
769 | })
|
770 | ```
|
771 |
|
772 | - ### **[isPromise(obj)](src/utils.js?source#L215)**
|
773 |
|
774 | **deprecate** Check if an object is a promise-like object.
|
775 | Don't use it to coercive a value to Promise, instead use `Promise.resolve`.
|
776 |
|
777 | - **<u>param</u>**: `obj` { _Any_ }
|
778 |
|
779 | - **<u>return</u>**: { _Boolean_ }
|
780 |
|
781 | - ### **[never()](src/utils.js?source#L221)**
|
782 |
|
783 | Create a promise that never ends.
|
784 |
|
785 | - **<u>return</u>**: { _Promise_ }
|
786 |
|
787 | A promise that will end the current pipeline.
|
788 |
|
789 | - ### **[promisify(fn, self)](src/utils.js?source#L250)**
|
790 |
|
791 | Convert a node callback style function to a function that returns
|
792 | promise when the last callback is not supplied.
|
793 |
|
794 | - **<u>param</u>**: `fn` { _Function_ }
|
795 |
|
796 | - **<u>param</u>**: `self` { _Any_ }
|
797 |
|
798 | The `this` to bind to the fn.
|
799 |
|
800 | - **<u>return</u>**: { _Function_ }
|
801 |
|
802 | - **<u>example</u>**:
|
803 |
|
804 | ```js
|
805 | var promisify = require('yaku/lib/promisify');
|
806 | function foo (val, cb) {
|
807 | setTimeout(() => {
|
808 | cb(null, val + 1);
|
809 | });
|
810 | }
|
811 |
|
812 | var bar = promisify(foo);
|
813 |
|
814 | bar(0).then((val) => {
|
815 | console.log val // output => 1
|
816 | });
|
817 |
|
818 | // It also supports the callback style.
|
819 | bar(0, (err, val) => {
|
820 | console.log(val); // output => 1
|
821 | });
|
822 | ```
|
823 |
|
824 | - ### **[sleep(time, val)](src/utils.js?source#L263)**
|
825 |
|
826 | Create a promise that will wait for a while before resolution.
|
827 |
|
828 | - **<u>param</u>**: `time` { _Integer_ }
|
829 |
|
830 | The unit is millisecond.
|
831 |
|
832 | - **<u>param</u>**: `val` { _Any_ }
|
833 |
|
834 | What the value this promise will resolve.
|
835 |
|
836 | - **<u>return</u>**: { _Promise_ }
|
837 |
|
838 | - **<u>example</u>**:
|
839 |
|
840 | ```js
|
841 | var sleep = require('yaku/lib/sleep');
|
842 | sleep(1000).then(() => console.log('after one second'));
|
843 | ```
|
844 |
|
845 | - ### **[Observable](src/utils.js?source#L269)**
|
846 |
|
847 | Read the `Observable` section.
|
848 |
|
849 | - **<u>type</u>**: { _Function_ }
|
850 |
|
851 | - ### **[retry(countdown, span, fn, this)](src/utils.js?source#L319)**
|
852 |
|
853 | Retry a function until it resolves before a mount of times, or reject with all
|
854 | the error states.
|
855 |
|
856 | - **<u>version_added</u>**:
|
857 |
|
858 | v0.7.10
|
859 |
|
860 | - **<u>param</u>**: `countdown` { _Number | Function_ }
|
861 |
|
862 | How many times to retry before rejection.
|
863 |
|
864 | - **<u>param</u>**: `span` { _Number_ }
|
865 |
|
866 | Optional. How long to wait before each retry in millisecond.
|
867 | When it's a function `(errs) => Boolean | Promise.resolve(Boolean)`,
|
868 | you can use it to create complex countdown logic,
|
869 | it can even return a promise to create async countdown logic.
|
870 |
|
871 | - **<u>param</u>**: `fn` { _Function_ }
|
872 |
|
873 | The function can return a promise or not.
|
874 |
|
875 | - **<u>param</u>**: `this` { _Any_ }
|
876 |
|
877 | Optional. The context to call the function.
|
878 |
|
879 | - **<u>return</u>**: { _Function_ }
|
880 |
|
881 | The wrapped function. The function will reject an array
|
882 | of reasons that throwed by each try.
|
883 |
|
884 | - **<u>example</u>**:
|
885 |
|
886 | Retry 3 times before rejection, wait 1 second before each retry.
|
887 | ```js
|
888 | var retry = require('yaku/lib/retry');
|
889 | var { request } = require('nokit');
|
890 |
|
891 | retry(3, 1000, request)('http://test.com').then(
|
892 | (body) => console.log(body),
|
893 | (errs) => console.error(errs)
|
894 | );
|
895 | ```
|
896 |
|
897 | - **<u>example</u>**:
|
898 |
|
899 | Here a more complex retry usage, it shows an random exponential backoff algorithm to
|
900 | wait and retry again, which means the 10th attempt may take 10 minutes to happen.
|
901 | ```js
|
902 | var retry = require('yaku/lib/retry');
|
903 | var sleep = require('yaku/lib/sleep');
|
904 | var { request } = require('nokit');
|
905 |
|
906 | function countdown (retries) {
|
907 | var attempt = 0;
|
908 | return async () => {
|
909 | var r = Math.random() * Math.pow(2, attempt) * 1000;
|
910 | var t = Math.min(r, 1000 * 60 * 10);
|
911 | await sleep(t);
|
912 | return attempt++ < retries;
|
913 | };
|
914 | }
|
915 |
|
916 | retry(countdown(10), request)('http://test.com').then(
|
917 | (body) => console.log(body),
|
918 | (errs) => console.error(errs)
|
919 | );
|
920 | ```
|
921 |
|
922 | - ### **[throw(err)](src/utils.js?source#L333)**
|
923 |
|
924 | Throw an error to break the program.
|
925 |
|
926 | - **<u>param</u>**: `err` { _Any_ }
|
927 |
|
928 | - **<u>example</u>**:
|
929 |
|
930 | ```js
|
931 | var ythrow = require('yaku/lib/throw');
|
932 | Promise.resolve().then(() => {
|
933 | // This error won't be caught by promise.
|
934 | ythrow('break the program!');
|
935 | });
|
936 | ```
|
937 |
|
938 | - ### **[timeout(promise, time, reason)](src/utils.js?source#L351)**
|
939 |
|
940 | Create a promise that will reject after a while if the passed in promise
|
941 | doesn't settle first.
|
942 |
|
943 | - **<u>param</u>**: `promise` { _Promise_ }
|
944 |
|
945 | The passed promise to wait.
|
946 |
|
947 | - **<u>param</u>**: `time` { _Integer_ }
|
948 |
|
949 | The unit is millisecond.
|
950 |
|
951 | - **<u>param</u>**: `reason` { _Any_ }
|
952 |
|
953 | After time out, it will be the reject reason.
|
954 |
|
955 | - **<u>return</u>**: { _Promise_ }
|
956 |
|
957 | - **<u>example</u>**:
|
958 |
|
959 | ```js
|
960 | var sleep = require('yaku/lib/sleep');
|
961 | var timeout = require('yaku/lib/timeout');
|
962 | timeout(sleep(500), 100).catch((err) => {
|
963 | console.error(err);
|
964 | });
|
965 | ```
|
966 |
|
967 |
|
968 |
|
969 |
|
970 | # Observable
|
971 |
|
972 | - ### **[Observable(executor)](src/Observable.js?source#L60)**
|
973 |
|
974 | Create a composable observable object.
|
975 | Promise can't resolve multiple times, this class makes it possible, so
|
976 | that you can easily map, filter and even back pressure events in a promise way.
|
977 | For live example: [Double Click Demo](https://jsbin.com/niwuti/edit?html,js,output).
|
978 |
|
979 | - **<u>version_added</u>**:
|
980 |
|
981 | v0.7.2
|
982 |
|
983 | - **<u>param</u>**: `executor` { _Function_ }
|
984 |
|
985 | `(next) ->` It's optional.
|
986 |
|
987 | - **<u>return</u>**: { _Observable_ }
|
988 |
|
989 | - **<u>example</u>**:
|
990 |
|
991 | ```js
|
992 | var Observable = require("yaku/lib/Observable");
|
993 | var linear = new Observable();
|
994 |
|
995 | var x = 0;
|
996 | setInterval(linear.next, 1000, x++);
|
997 |
|
998 | // Wait for 2 sec then emit the next value.
|
999 | var quad = linear.subscribe(async x => {
|
1000 | await sleep(2000);
|
1001 | return x * x;
|
1002 | });
|
1003 |
|
1004 | var another = linear.subscribe(x => -x);
|
1005 |
|
1006 | quad.subscribe(
|
1007 | value => { console.log(value); },
|
1008 | reason => { console.error(reason); }
|
1009 | );
|
1010 |
|
1011 | // Emit error
|
1012 | linear.error(new Error("reason"));
|
1013 |
|
1014 | // Unsubscribe an observable.
|
1015 | quad.unsubscribe();
|
1016 |
|
1017 | // Unsubscribe all subscribers.
|
1018 | linear.subscribers = [];
|
1019 | ```
|
1020 |
|
1021 | - **<u>example</u>**:
|
1022 |
|
1023 | Use it with DOM.
|
1024 | ```js
|
1025 | var filter = fn => v => fn(v) ? v : new Promise(() => {});
|
1026 |
|
1027 | var keyup = new Observable((next) => {
|
1028 | document.querySelector('input').onkeyup = next;
|
1029 | });
|
1030 |
|
1031 | var keyupText = keyup.subscribe(e => e.target.value);
|
1032 |
|
1033 | // Now we only get the input when the text length is greater than 3.
|
1034 | var keyupTextGT3 = keyupText.subscribe(filter(text => text.length > 3));
|
1035 |
|
1036 | keyupTextGT3.subscribe(v => console.log(v));
|
1037 | ```
|
1038 |
|
1039 | - ### **[next(value)](src/Observable.js?source#L77)**
|
1040 |
|
1041 | Emit a value.
|
1042 |
|
1043 | - **<u>param</u>**: `value` { _Any_ }
|
1044 |
|
1045 | so that the event will go to `onError` callback.
|
1046 |
|
1047 | - ### **[error(value)](src/Observable.js?source#L83)**
|
1048 |
|
1049 | Emit an error.
|
1050 |
|
1051 | - **<u>param</u>**: `value` { _Any_ }
|
1052 |
|
1053 | - ### **[publisher](src/Observable.js?source#L89)**
|
1054 |
|
1055 | The publisher observable of this.
|
1056 |
|
1057 | - **<u>type</u>**: { _Observable_ }
|
1058 |
|
1059 | - ### **[subscribers](src/Observable.js?source#L95)**
|
1060 |
|
1061 | All the subscribers subscribed this observable.
|
1062 |
|
1063 | - **<u>type</u>**: { _Array_ }
|
1064 |
|
1065 | - ### **[subscribe(onNext, onError)](src/Observable.js?source#L103)**
|
1066 |
|
1067 | It will create a new Observable, like promise.
|
1068 |
|
1069 | - **<u>param</u>**: `onNext` { _Function_ }
|
1070 |
|
1071 | - **<u>param</u>**: `onError` { _Function_ }
|
1072 |
|
1073 | - **<u>return</u>**: { _Observable_ }
|
1074 |
|
1075 | - ### **[unsubscribe](src/Observable.js?source#L118)**
|
1076 |
|
1077 | Unsubscribe this.
|
1078 |
|
1079 | - ### **[Observable.merge(iterable)](src/Observable.js?source#L173)**
|
1080 |
|
1081 | Merge multiple observables into one.
|
1082 |
|
1083 | - **<u>version_added</u>**:
|
1084 |
|
1085 | 0.9.6
|
1086 |
|
1087 | - **<u>param</u>**: `iterable` { _Iterable_ }
|
1088 |
|
1089 | - **<u>return</u>**: { _Observable_ }
|
1090 |
|
1091 | - **<u>example</u>**:
|
1092 |
|
1093 | ```js
|
1094 | var Observable = require("yaku/lib/Observable");
|
1095 | var sleep = require("yaku/lib/sleep");
|
1096 |
|
1097 | var src = new Observable(next => setInterval(next, 1000, 0));
|
1098 |
|
1099 | var a = src.subscribe(v => v + 1; });
|
1100 | var b = src.subscribe((v) => sleep(10, v + 2));
|
1101 |
|
1102 | var out = Observable.merge([a, b]);
|
1103 |
|
1104 | out.subscribe((v) => {
|
1105 | console.log(v);
|
1106 | })
|
1107 | ```
|
1108 |
|
1109 |
|
1110 |
|
1111 |
|
1112 |
|
1113 | # Unit Test
|
1114 |
|
1115 | This project use [promises-aplus-tests][] to test the compliance of Promises/A+ specification. There are about 900 test cases.
|
1116 |
|
1117 | Use `npm run no -- test` to run the unit test against yaku.
|
1118 |
|
1119 | ## Test other libs
|
1120 |
|
1121 | ### basic test
|
1122 |
|
1123 | To test `bluebird`: `npm run no -- test-basic --shim bluebird`
|
1124 |
|
1125 | The `bluebird` can be replaced with other lib, see the `test/getPromise.js` for which libs are supported.
|
1126 |
|
1127 | ### aplus test
|
1128 |
|
1129 | To test `bluebird`: `npm run no -- test-aplus --shim bluebird`
|
1130 |
|
1131 | The `bluebird` can be replaced with other lib, see the `test/getPromise.js` for which libs are supported.
|
1132 |
|
1133 | ### es6 test
|
1134 |
|
1135 | To test `bluebird`: `npm run no -- test-es6 --shim bluebird`
|
1136 |
|
1137 | The `bluebird` can be replaced with other lib, see the `test/getPromise.js` for which libs are supported.
|
1138 |
|
1139 |
|
1140 | # Benchmark
|
1141 |
|
1142 | Use `npm run no -- benchmark` to run the benchmark.
|
1143 |
|
1144 | ## async/await generator wrapper
|
1145 |
|
1146 | ```
|
1147 | Node v5.6.0
|
1148 | OS darwin
|
1149 | Arch x64
|
1150 | CPU Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
|
1151 |
|
1152 | yaku: 117ms
|
1153 | co: 283ms
|
1154 | bluebird: 643ms
|
1155 | ```
|
1156 |
|
1157 | # Contribution
|
1158 |
|
1159 | Make sure you have `npm` and `npm install` at the root of the project first.
|
1160 |
|
1161 | Other than use `gulp`, all my projects use [nokit][] to deal with automation.
|
1162 | Run `npm run no -- -h` to print all the tasks that you can use.
|
1163 |
|
1164 | ## Update `readme.md`
|
1165 |
|
1166 | Please don't alter the `readme.md` directly, it is compiled from the `docs/readme.jst.md`.
|
1167 | Edit the `docs/readme.jst.md` and execute `npm run no` to rebuild the project.
|
1168 |
|
1169 | [docs/lazyTree.md]: docs/lazyTree.md
|
1170 | [docs/debugHelperComparison.md]: docs/debugHelperComparison.md
|
1171 | [Bluebird]: https://github.com/petkaantonov/bluebird
|
1172 | [ES6-promise]: https://github.com/jakearchibald/es6-promise
|
1173 | [pinkie]: https://github.com/floatdrop/pinkie
|
1174 | [core-js tests]: https://github.com/ysmood/core-js/tree/promise-yaku
|
1175 | [native]: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-promise-objects
|
1176 | [q]: https://github.com/kriskowal/q
|
1177 | [my-promise]: https://github.com/hax/my-promise
|
1178 | [core-js]: https://github.com/zloirock/core-js
|
1179 | [yaku]: https://github.com/ysmood/yaku
|
1180 | [yaku.core]: src/yaku.core.js
|
1181 | [yaku.aplus]: src/yaku.aplus.js
|
1182 | [es6-shim]: https://github.com/paulmillr/es6-shim
|
1183 | [release page]: https://github.com/ysmood/yaku/releases
|
1184 | [docs/minPromiseAplus.js]: docs/minPromiseAplus.js
|
1185 | [promises-aplus-tests]: https://github.com/promises-aplus/promises-tests
|
1186 | [promises-es6-tests]: https://github.com/promises-es6/promises-es6
|
1187 | [longjohn]: https://github.com/mattinsler/longjohn
|
1188 | [crhome-lst]: http://www.html5rocks.com/en/tutorials/developertools/async-call-stack
|
1189 | [Browserify]: http://browserify.org
|
1190 | [Webpack]: http://webpack.github.io/
|
1191 | [nokit]: https://github.com/ysmood/nokit
|
1192 | [nofile.js]: nofile.js |
\ | No newline at end of file |