UNPKG

35.1 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- 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
62Use something like [Browserify][] or [Webpack][], or download the `yaku.js` file from [release page][].
63Raw 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
83These comparisons only reflect some limited truth, no one is better than all others on all aspects.
84There 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```
87Date: Sat Dec 17 2016 22:15:40 GMT+0800 (CST)
88Node v7.2.1
89OS darwin
90Arch x64
91CPU 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
143Yaku will report any unhandled rejection via `console.error` by default, in case you forget to write `catch`.
144You can catch them manually:
145
146- Browser: `window.onunhandledrejection = ({ promise, reason }) => { /* Your Code */ };`
147- Node: `process.on("unhandledRejection", (reason, promise) => { /* Your Code */ });`
148
149For 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
520It's a bundle of all the following functions. You can require them all with `var yutils = require("yaku/lib/utils")`,
521or 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
524require("yaku/lib/_").Promise = require("bluebird");
525var 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
1115This project use [promises-aplus-tests][] to test the compliance of Promises/A+ specification. There are about 900 test cases.
1116
1117Use `npm run no -- test` to run the unit test against yaku.
1118
1119## Test other libs
1120
1121### basic test
1122
1123To test `bluebird`: `npm run no -- test-basic --shim bluebird`
1124
1125The `bluebird` can be replaced with other lib, see the `test/getPromise.js` for which libs are supported.
1126
1127### aplus test
1128
1129To test `bluebird`: `npm run no -- test-aplus --shim bluebird`
1130
1131The `bluebird` can be replaced with other lib, see the `test/getPromise.js` for which libs are supported.
1132
1133### es6 test
1134
1135To test `bluebird`: `npm run no -- test-es6 --shim bluebird`
1136
1137The `bluebird` can be replaced with other lib, see the `test/getPromise.js` for which libs are supported.
1138
1139
1140# Benchmark
1141
1142Use `npm run no -- benchmark` to run the benchmark.
1143
1144## async/await generator wrapper
1145
1146```
1147Node v5.6.0
1148OS darwin
1149Arch x64
1150CPU Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
1151
1152yaku: 117ms
1153co: 283ms
1154bluebird: 643ms
1155```
1156
1157# Contribution
1158
1159Make sure you have `npm` and `npm install` at the root of the project first.
1160
1161Other than use `gulp`, all my projects use [nokit][] to deal with automation.
1162Run `npm run no -- -h` to print all the tasks that you can use.
1163
1164## Update `readme.md`
1165
1166Please don't alter the `readme.md` directly, it is compiled from the `docs/readme.jst.md`.
1167Edit 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