Comparing version 0.9.2 to 0.9.3
var _ = require("./_"); | ||
var genIterator = require("./genIterator"); | ||
/** | ||
* Create a composable observable object. | ||
* Promise can't resolve multiple times, this function makes it possible, so | ||
* that you can easily map, filter and even back pressure events in a promise way. | ||
* For real world example: [Double Click Demo](https://jsfiddle.net/ysmood/musds0sv/). | ||
* @version_added v0.7.2 | ||
* @param {Function} executor `(emit) ->` It's optional. | ||
* @return {Observable} | ||
* @example | ||
* ```js | ||
* var Observable = require("yaku/lib/Observable"); | ||
* var linear = new Observable(); | ||
* | ||
* var x = 0; | ||
* setInterval(linear.emit, 1000, x++); | ||
* | ||
* // Wait for a moment then emit the value. | ||
* var quad = linear.subscribe(async x => { | ||
* await sleep(2000); | ||
* return x * x; | ||
* }); | ||
* | ||
* var another = linear.subscribe(x => -x); | ||
* | ||
* quad.subscribe( | ||
* value => { console.log(value); }, | ||
* reason => { console.error(reason); } | ||
* ); | ||
* | ||
* // Emit error | ||
* linear.emit(Promise.reject(new Error("reason"))); | ||
* | ||
* // Unsubscribe a observable. | ||
* quad.unsubscribe(); | ||
* | ||
* // Unsubscribe all children. | ||
* linear.children = []; | ||
* ``` | ||
* @example | ||
* Use it with DOM. | ||
* ```js | ||
* var filter = fn => v => fn(v) ? v : new Promise(() => {}); | ||
* | ||
* var keyup = new Observable((emit) => { | ||
* document.querySelector('input').onkeyup = emit; | ||
* }); | ||
* | ||
* var keyupText = keyup.subscribe(e => e.target.value); | ||
* | ||
* // Now we only get the input when the text length is greater than 3. | ||
* var keyupTextGT3 = keyupText.subscribe(filter(text => text.length > 3)); | ||
* | ||
* keyupTextGT3.subscribe(v => console.log(v)); | ||
* ``` | ||
* @example | ||
* Merge two sources into one. | ||
* ```js | ||
* let one = new Observable(emit => setInterval(emit, 100, 'one')); | ||
* let two = new Observable(emit => setInterval(emit, 200, 'two')); | ||
* let merge = list => new Observable( | ||
* (emit) => list.forEach(o => o.subscribe(emit)) | ||
* ); | ||
* | ||
* let three = merge([one, two]); | ||
* three.subscribe(v => console.log(v)); | ||
* ``` | ||
*/ | ||
var Observable = module.exports = function Observable (executor) { | ||
@@ -13,4 +81,27 @@ var self = this | ||
_.extendPrototype(Observable, { | ||
/** | ||
* Emit a value. | ||
* @param {Any} value | ||
*/ | ||
emit: null, | ||
/** | ||
* The parent observable of this. | ||
* @type {Observable} | ||
*/ | ||
parent: null, | ||
/** | ||
* All the children subscribed this observable. | ||
* @type {Array} | ||
*/ | ||
children: null, | ||
/** | ||
* It will create a new Observable, like promise. | ||
* @param {Function} onEmit | ||
* @param {Function} onError | ||
* @return {Observable} | ||
*/ | ||
subscribe: function (onEmit, onError) { | ||
@@ -28,2 +119,5 @@ var self = this, child = new Observable(); | ||
/** | ||
* Unsubscribe this. | ||
*/ | ||
unsubscribe: function () { | ||
@@ -57,1 +151,52 @@ var parent = this.parent; | ||
} | ||
/** | ||
* Merge multiple observables into one. | ||
* @param {Iterable} iterable | ||
* @return {Observable} | ||
* @example | ||
* ```js | ||
* var Observable = require("yaku/lib/Observable"); | ||
* var sleep = require("yaku/lib/sleep"); | ||
* | ||
* var src = new Observable(function (emit) { | ||
* setInterval(emit, 1000, 0); | ||
* }); | ||
* | ||
* var a = src.subscribe(function (v) { return v + 1; }); | ||
* var b = src.subscribe(function (v) { | ||
* return sleep(10, v + 2); | ||
* }); | ||
* | ||
* var out = Observable.all([a, b]); | ||
* | ||
* out.subscribe(function (arr) { | ||
* console.log(arr); | ||
* }) | ||
* ``` | ||
*/ | ||
Observable.all = function all (iterable) { | ||
var iter = genIterator(iterable); | ||
return new Observable(function (emit) { | ||
var result = [], len = iterable.length, item; | ||
function onEmit (v) { | ||
result.push(v); | ||
if (result.length === len) { | ||
emit(result); | ||
result = []; | ||
} | ||
} | ||
function onError (e) { | ||
emit(_.Promise.reject(e)); | ||
result = []; | ||
} | ||
while (!(item = iter.next()).done) { | ||
item.value.subscribe(onEmit, onError); | ||
} | ||
}); | ||
}; |
@@ -201,85 +201,4 @@ // This file contains all the non-ES6-standard helpers based on promise. | ||
/** | ||
* Create a composable observable object. | ||
* Promise can't resolve multiple times, this function makes it possible, so | ||
* that you can easily map, filter and even back pressure events in a promise way. | ||
* For real world example: [Double Click Demo](https://jsfiddle.net/ysmood/musds0sv/). | ||
* @version_added v0.7.2 | ||
* @param {Function} executor `(emit) ->` It's optional. | ||
* @return {Object} The observable object's members: | ||
* ```js | ||
* { | ||
* // It will create a new Observable, like promise. | ||
* subscribe: (onEmit, onError) => Observable, | ||
* | ||
* // Unsubscribe this. | ||
* unsubscribe: () => {}, | ||
* | ||
* // Emit a value | ||
* emit: (value) => {}, | ||
* | ||
* // The parent observable of this. | ||
* parent: Observable || null, | ||
* | ||
* // All the children subscribed this observable. | ||
* children: Array | ||
* } | ||
* ``` | ||
* @example | ||
* ```js | ||
* var Observable = require("yaku/lib/Observable"); | ||
* var linear = new Observable(); | ||
* | ||
* var x = 0; | ||
* setInterval(linear.emit, 1000, x++); | ||
* | ||
* // Wait for a moment then emit the value. | ||
* var quad = linear.subscribe(async x => { | ||
* await sleep(2000); | ||
* return x * x; | ||
* }); | ||
* | ||
* var another = linear.subscribe(x => -x); | ||
* | ||
* quad.subscribe( | ||
* value => { console.log(value); }, | ||
* reason => { console.error(reason); } | ||
* ); | ||
* | ||
* // Emit error | ||
* linear.emit(Promise.reject(new Error("reason"))); | ||
* | ||
* // Unsubscribe a observable. | ||
* quad.unsubscribe(); | ||
* | ||
* // Unsubscribe all children. | ||
* linear.children = []; | ||
* ``` | ||
* @example | ||
* Use it with DOM. | ||
* ```js | ||
* var filter = fn => v => fn(v) ? v : new Promise(() => {}); | ||
* | ||
* var keyup = new Observable((emit) => { | ||
* document.querySelector('input').onkeyup = emit; | ||
* }); | ||
* | ||
* var keyupText = keyup.subscribe(e => e.target.value); | ||
* | ||
* // Now we only get the input when the text length is greater than 3. | ||
* var keyupTextGT3 = keyupText.subscribe(filter(text => text.length > 3)); | ||
* | ||
* keyupTextGT3.subscribe(v => console.log(v)); | ||
* ``` | ||
* @example | ||
* Merge two sources into one. | ||
* ```js | ||
* let one = new Observable(emit => setInterval(emit, 100, 'one')); | ||
* let two = new Observable(emit => setInterval(emit, 200, 'two')); | ||
* let merge = list => new Observable( | ||
* (emit) => list.forEach(o => o.subscribe(emit)) | ||
* ); | ||
* | ||
* let three = merge([one, two]); | ||
* three.subscribe(v => console.log(v)); | ||
* ``` | ||
* Read the `Observable` section. | ||
* @type {Function} | ||
*/ | ||
@@ -286,0 +205,0 @@ Observable: require("./Observable"), |
/* | ||
Yaku v0.9.2 | ||
Yaku v0.9.3 | ||
(c) 2015 Yad Smood. http://ysmood.org | ||
@@ -4,0 +4,0 @@ License MIT |
{ | ||
"name": "yaku", | ||
"version": "0.9.2", | ||
"version": "0.9.3", | ||
"description": "A light-weight ES6 Promises/A+ implementation that doesn't hurt.", | ||
@@ -5,0 +5,0 @@ "main": "lib/yaku.js", |
209
readme.md
@@ -673,42 +673,116 @@ <a href="http://promisesaplus.com/"> | ||
- ### **[Observable(executor)](src/utils.js?source#L285)** | ||
- ### **[Observable](src/utils.js?source#L204)** | ||
Create a composable observable object. | ||
Promise can't resolve multiple times, this function makes it possible, so | ||
that you can easily map, filter and even back pressure events in a promise way. | ||
For real world example: [Double Click Demo](https://jsfiddle.net/ysmood/musds0sv/). | ||
Read the `Observable` section. | ||
- **<u>type</u>**: { _Function_ } | ||
- ### **[retry(countdown, fn, this)](src/utils.js?source#L253)** | ||
Retry a function until it resolves before a mount of times, or reject with all | ||
the error states. | ||
- **<u>version_added</u>**: | ||
v0.7.2 | ||
v0.7.10 | ||
- **<u>param</u>**: `executor` { _Function_ } | ||
- **<u>param</u>**: `countdown` { _Number | Function_ } | ||
`(emit) ->` It's optional. | ||
How many times to retry before rejection. | ||
When it's a function `(errs) => Boolean | Promise.resolve(Boolean)`, | ||
you can use it to create complex countdown logic, | ||
it can even return a promise to create async countdown logic. | ||
- **<u>return</u>**: { _Object_ } | ||
- **<u>param</u>**: `fn` { _Function_ } | ||
The observable object's members: | ||
The function can return a promise or not. | ||
- **<u>param</u>**: `this` { _Any_ } | ||
Optional. The context to call the function. | ||
- **<u>return</u>**: { _Function_ } | ||
The wrapped function. The function will reject an array | ||
of reasons that throwed by each try. | ||
- **<u>example</u>**: | ||
Retry 3 times before rejection. | ||
```js | ||
{ | ||
// It will create a new Observable, like promise. | ||
subscribe: (onEmit, onError) => Observable, | ||
var retry = require('yaku/lib/retry'); | ||
var { request } = require('nokit'); | ||
// Unsubscribe this. | ||
unsubscribe: () => {}, | ||
retry(3, request)('http://test.com').then( | ||
(body) => console.log(body), | ||
(errs) => console.error(errs) | ||
); | ||
``` | ||
// Emit a value | ||
emit: (value) => {}, | ||
- **<u>example</u>**: | ||
// The parent observable of this. | ||
parent: Observable || null, | ||
Here a more complex retry usage, it shows an random exponential backoff algorithm to | ||
wait and retry again, which means the 10th attempt may take 10 minutes to happen. | ||
```js | ||
var retry = require('yaku/lib/retry'); | ||
var sleep = require('yaku/lib/sleep'); | ||
var { request } = require('nokit'); | ||
// All the children subscribed this observable. | ||
children: Array | ||
function countdown (retries) { | ||
var attempt = 0; | ||
return async () => { | ||
var r = Math.random() * Math.pow(2, attempt) * 1000; | ||
var t = Math.min(r, 1000 * 60 * 10); | ||
await sleep(t); | ||
attempt++ < retries; | ||
}; | ||
} | ||
retry(countdown(10), request)('http://test.com').then( | ||
(body) => console.log(body), | ||
(errs) => console.error(errs) | ||
); | ||
``` | ||
- ### **[throw(err)](src/utils.js?source#L267)** | ||
Throw an error to break the program. | ||
- **<u>param</u>**: `err` { _Any_ } | ||
- **<u>example</u>**: | ||
```js | ||
var ythrow = require('yaku/lib/throw'); | ||
Promise.resolve().then(() => { | ||
// This error won't be caught by promise. | ||
ythrow('break the program!'); | ||
}); | ||
``` | ||
# Observable | ||
- ### **[Observable(executor)](src/Observable.js?source#L71)** | ||
Create a composable observable object. | ||
Promise can't resolve multiple times, this function makes it possible, so | ||
that you can easily map, filter and even back pressure events in a promise way. | ||
For real world example: [Double Click Demo](https://jsfiddle.net/ysmood/musds0sv/). | ||
- **<u>version_added</u>**: | ||
v0.7.2 | ||
- **<u>param</u>**: `executor` { _Function_ } | ||
`(emit) ->` It's optional. | ||
- **<u>return</u>**: { _Observable_ } | ||
- **<u>example</u>**: | ||
```js | ||
var Observable = require("yaku/lib/Observable"); | ||
@@ -775,83 +849,62 @@ var linear = new Observable(); | ||
- ### **[retry(countdown, fn, this)](src/utils.js?source#L334)** | ||
- ### **[emit(value)](src/Observable.js?source#L86)** | ||
Retry a function until it resolves before a mount of times, or reject with all | ||
the error states. | ||
Emit a value. | ||
- **<u>version_added</u>**: | ||
- **<u>param</u>**: `value` { _Any_ } | ||
v0.7.10 | ||
- ### **[parent](src/Observable.js?source#L92)** | ||
- **<u>param</u>**: `countdown` { _Number | Function_ } | ||
The parent observable of this. | ||
How many times to retry before rejection. | ||
When it's a function `(errs) => Boolean | Promise.resolve(Boolean)`, | ||
you can use it to create complex countdown logic, | ||
it can even return a promise to create async countdown logic. | ||
- **<u>type</u>**: { _Observable_ } | ||
- **<u>param</u>**: `fn` { _Function_ } | ||
- ### **[children](src/Observable.js?source#L98)** | ||
The function can return a promise or not. | ||
All the children subscribed this observable. | ||
- **<u>param</u>**: `this` { _Any_ } | ||
- **<u>type</u>**: { _Array_ } | ||
Optional. The context to call the function. | ||
- ### **[subscribe(onEmit, onError)](src/Observable.js?source#L106)** | ||
- **<u>return</u>**: { _Function_ } | ||
It will create a new Observable, like promise. | ||
The wrapped function. The function will reject an array | ||
of reasons that throwed by each try. | ||
- **<u>param</u>**: `onEmit` { _Function_ } | ||
- **<u>example</u>**: | ||
- **<u>param</u>**: `onError` { _Function_ } | ||
Retry 3 times before rejection. | ||
```js | ||
var retry = require('yaku/lib/retry'); | ||
var { request } = require('nokit'); | ||
- **<u>return</u>**: { _Observable_ } | ||
retry(3, request)('http://test.com').then( | ||
(body) => console.log(body), | ||
(errs) => console.error(errs) | ||
); | ||
``` | ||
- ### **[unsubscribe](src/Observable.js?source#L121)** | ||
- **<u>example</u>**: | ||
Unsubscribe this. | ||
Here a more complex retry usage, it shows an random exponential backoff algorithm to | ||
wait and retry again, which means the 10th attempt may take 10 minutes to happen. | ||
```js | ||
var retry = require('yaku/lib/retry'); | ||
var sleep = require('yaku/lib/sleep'); | ||
var { request } = require('nokit'); | ||
- ### **[Observable.all(iterable)](src/Observable.js?source#L175)** | ||
function countdown (retries) { | ||
var attempt = 0; | ||
return async () => { | ||
var r = Math.random() * Math.pow(2, attempt) * 1000; | ||
var t = Math.min(r, 1000 * 60 * 10); | ||
await sleep(t); | ||
attempt++ < retries; | ||
}; | ||
} | ||
Merge multiple observables into one. | ||
retry(countdown(10), request)('http://test.com').then( | ||
(body) => console.log(body), | ||
(errs) => console.error(errs) | ||
); | ||
``` | ||
- **<u>param</u>**: `iterable` { _Iterable_ } | ||
- ### **[throw(err)](src/utils.js?source#L348)** | ||
- **<u>return</u>**: { _Observable_ } | ||
Throw an error to break the program. | ||
- **<u>param</u>**: `err` { _Any_ } | ||
- **<u>example</u>**: | ||
```js | ||
var ythrow = require('yaku/lib/throw'); | ||
Promise.resolve().then(() => { | ||
// This error won't be caught by promise. | ||
ythrow('break the program!'); | ||
var Observable = require("yaku/lib/Observable"); | ||
var sleep = require("yaku/lib/sleep"); | ||
var src = new Observable(function (emit) { | ||
setInterval(emit, 1000, 0); | ||
}); | ||
var a = src.subscribe(function (v) { return v + 1; }); | ||
var b = src.subscribe(function (v) { | ||
return sleep(10, v + 2); | ||
}); | ||
var out = Observable.all([a, b]); | ||
out.subscribe(function (arr) { | ||
console.log(arr); | ||
}) | ||
``` | ||
@@ -858,0 +911,0 @@ |
73798
1393
950