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