UNPKG

34.9 kBMarkdownView Raw
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
8Yaku is full compatible with ES6's native [Promise][native], but much faster, and more error friendly.
9If 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).
10It only implements the `constructor` and `then`.
11
12Yaku passed all the tests of [promises-aplus-tests][], [promises-es6-tests][], and even the [core-js tests][].
13
14I am not an optimization freak, I try to keep the source code readable and maintainable.
15I 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
39npm install yaku
40```
41
42Then:
43
44```js
45var Promise = require('yaku');
46```
47
48Or if you don't want any extra debug helper, ES6 only version is here:
49
50```js
51var Promise = require('yaku/lib/yaku.core');
52```
53
54Or if you only want aplus support:
55
56```js
57var Promise = require('yaku/lib/yaku.aplus');
58```
59
60## Browser
61
62Raw 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
82These comparisons only reflect some limited truth, no one is better than all others on all aspects.
83There 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```
86Date: Sat Dec 17 2016 22:15:40 GMT+0800 (CST)
87Node v7.2.1
88OS darwin
89Arch x64
90CPU 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
138Yaku will report any unhandled rejection via `console.error` by default, in case you forget to write `catch`.
139You can catch them manually:
140
141- Browser: `window.onunhandledrejection = ({ promise, reason }) => { /* Your Code */ };`
142- Node: `process.on("unhandledRejection", (reason, promise) => { /* Your Code */ });`
143
144For 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
515It's a bundle of all the following functions. You can require them all with `var yutils = require("yaku/lib/utils")`,
516or 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
519require("yaku/lib/_").Promise = require("bluebird");
520var 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
1110This project use [promises-aplus-tests][] to test the compliance of Promises/A+ specification. There are about 900 test cases.
1111
1112Use `npm run no -- test` to run the unit test against yaku.
1113
1114## Test other libs
1115
1116### basic test
1117
1118To test `bluebird`: `npm run no -- test-basic --shim bluebird`
1119
1120The `bluebird` can be replaced with other lib, see the `test/getPromise.js` for which libs are supported.
1121
1122### aplus test
1123
1124To test `bluebird`: `npm run no -- test-aplus --shim bluebird`
1125
1126The `bluebird` can be replaced with other lib, see the `test/getPromise.js` for which libs are supported.
1127
1128### es6 test
1129
1130To test `bluebird`: `npm run no -- test-es6 --shim bluebird`
1131
1132The `bluebird` can be replaced with other lib, see the `test/getPromise.js` for which libs are supported.
1133
1134
1135# Benchmark
1136
1137Use `npm run no -- benchmark` to run the benchmark.
1138
1139## async/await generator wrapper
1140
1141```
1142Node v5.6.0
1143OS darwin
1144Arch x64
1145CPU Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
1146
1147yaku: 117ms
1148co: 283ms
1149bluebird: 643ms
1150```
1151
1152# Contribution
1153
1154Make sure you have `npm` and `npm install` at the root of the project first.
1155
1156Other than use `gulp`, all my projects use [nokit][] to deal with automation.
1157Run `npm run no -- -h` to print all the tasks that you can use.
1158
1159## Update `readme.md`
1160
1161Please don't alter the `readme.md` directly, it is compiled from the `docs/readme.jst.md`.
1162Edit 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