forked from baconjs/bacon.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
readme-src.coffee
2037 lines (1606 loc) · 74.6 KB
/
readme-src.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
doc = new (require "./readme/doc.coffee")
doc.section "Bacon.js"
doc.logo()
doc.text """
A small functional reactive programming lib for JavaScript.
Turns your event spaghetti into clean and declarative feng shui bacon, by switching
from imperative to functional. It's like replacing nested for-loops with functional programming
concepts like [`map`](#observable-map) and `filter`. Stop working on individual events and work with event streams instead.
Combine your data with `merge` and `combine`.
Then switch to the heavier weapons and wield `flatMap` and `combineTemplate` like a boss.
It's the `_` of Events. Too bad the symbol `~` is not allowed in JavaScript.
Here's the stuff.
- [Homepage](https://baconjs.github.io/)
- [CoffeeScript source](https://github.com/baconjs/bacon.js/tree/master/src)
- [Generated javascript](https://github.com/baconjs/bacon.js/blob/master/dist/)
- [Specs](https://github.com/baconjs/bacon.js/blob/master/spec/specs/main.coffee)
- [Examples](https://github.com/baconjs/bacon.js/blob/master/examples/examples.html)
- [Wiki](https://github.com/baconjs/bacon.js/wiki/) with more docs, related projects and more
- [Cheat Sheet](https://www.cheatography.com/proloser/cheat-sheets/bacon-js/)
- [My Blog](https://nullzzz.blogspot.com) with some baconful and reactive postings along with a Bacon.js tutorial
- [Bacon.js Blog](https://baconjs.blogspot.com)
- [Bacon.js Google Group](https://groups.google.com/forum/#!forum/baconjs) for discussion and questions
- [TodoMVC with Bacon.js and jQuery](https://github.com/raimohanska/todomvc/blob/bacon-jquery/labs/architecture-examples/baconjs/js/app.js)
- [Stack Overflow](https://stackoverflow.com/questions/tagged/bacon.js) for well-formed questions. Use the "bacon.js" tag.
- [Gitter](https://gitter.im/baconjs/bacon.js) chat for developers of Bacon.
You can also check out my entertaining (LOL), interactive, solid-ass [slideshow](https://raimohanska.github.com/bacon.js-slides/).
And remember to give me feedback on the bacon! Let me know if you've
used it. Tell me how it worked for you. What's missing? What's wrong?
Please contribute!
[![Build Status](https://travis-ci.org/baconjs/bacon.js.svg?branch=master)](https://travis-ci.org/baconjs/bacon.js)
[![NPM version](https://img.shields.io/npm/v/baconjs.svg)](https://www.npmjs.org/package/baconjs)
[![NuGet version](https://img.shields.io/nuget/v/Bacon.js.svg)](https://www.nuget.org/packages/Bacon.js)
[![Dependency Status](https://david-dm.org/baconjs/bacon.js.svg)](https://david-dm.org/baconjs/bacon.js)
[![devDependency Status](https://david-dm.org/baconjs/bacon.js/dev-status.svg)](https://david-dm.org/baconjs/bacon.js#info=devDependencies)
"""
doc.toc()
doc.section "Install"
doc.text """
If you're targeting to [node.js](https://nodejs.org/), you can
npm install baconjs
For [bower](https://github.com/twitter/bower) users:
bower install bacon
Both minified and unminified versions available on [cdnjs](https://cdnjs.com/libraries/bacon.js).
Starting from 0.7.45, you can build your own Bacon.js bundle with selected features
only. See instructions [here](#build).
Prefer to drink from the firehose? Download from Github [master](https://raw.github.com/baconjs/bacon.js/master/dist/Bacon.js).
Visual Studio users can obtain version 0.7.16 via NuGet Packages
https://www.nuget.org/packages/Bacon.js/0.7.16
"""
doc.section "Intro"
doc.text """
The idea of Functional Reactive Programming is quite well described by Conal Elliot at [Stack Overflow](https://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming/1030631#1030631).
Bacon.js is a library for functional reactive programming. Or let's say it's a library for
working with [events](#event) and dynamic values (which are called [Properties](#property) in Bacon.js).
Anyways, you can wrap an event source,
say "mouse clicks on an element" into an `EventStream` by saying
```js
var clicks = $("h1").asEventStream("click")
```
Each EventStream represents a stream of events. It is an Observable object, meaning
that you can listen to events in the stream using, for instance, the [`onValue`](#stream-onvalue) method
with a callback. Like this:
```js
clicks.onValue(function() { alert("you clicked the h1 element") })
```
But you can do neater stuff too. The Bacon of bacon.js is in that you can transform,
filter and combine these streams in a multitude of ways (see API below). The methods [`map`](#observable-map),
[`filter`](#observable-filter), for example, are similar to same functions in functional list programming
(like [Underscore](https://documentcloud.github.com/underscore/)). So, if you say
```js
var plus = $("#plus").asEventStream("click").map(1)
var minus = $("#minus").asEventStream("click").map(-1)
var both = plus.merge(minus)
```
.. you'll have a stream that will output the number 1 when the "plus" button is clicked
and another stream outputting -1 when the "minus" button is clicked. The `both` stream will
be a merged stream containing events from both the plus and minus streams. This allows
you to subscribe to both streams with one handler:
```js
both.onValue(function(val) { /* val will be 1 or -1 */ })
```
In addition to EventStreams, bacon.js has a thing called [`Property`](#property), that is almost like an
EventStream, but has a "current value". So things that change and have a current state are
Properties, while things that consist of discrete events are EventStreams. You could think
mouse clicks as an EventStream and mouse position as a Property. You can create Properties from
an EventStream with [`scan`](#observable-scan) or [`toProperty`](#stream-toproperty) methods. So, let's say
```js
function add(x, y) { return x + y }
var counter = both.scan(0, add)
counter.onValue(function(sum) { $("#sum").text(sum) })
```
The `counter` property will contain the sum of the values in the `both` stream, so it's practically
a counter that can be increased and decreased using the plus and minus buttons. The [`scan`](#observable-scan) method
was used here to calculate the "current sum" of events in the `both` stream, by giving a "seed value"
`0` and an "accumulator function" `add`. The scan method creates a property that starts with the given
seed value and on each event in the source stream applies the accumulator function to the current
property value and the new value from the stream.
Properties can be very conveniently used for assigning values and attributes to DOM elements with JQuery.
Here we assign the value of a property as the text of a span element whenever it changes:
```js
property.assign($("span"), "text")
```
Hiding and showing the same span depending on the content of the property value is equally straightforward
```js
function hiddenForEmptyValue(value) { return value == "" ? "hidden" : "visible" }
property.map(hiddenForEmptyValue).assign($("span"), "css", "visibility")
```
In the example above a property value of "hello" would be mapped to "visible", which in turn would result in Bacon calling
```js
$("span").css("visibility", "visible")
```
For an actual tutorial, please check out my [blog posts](https://nullzzz.blogspot.fi/2012/11/baconjs-tutorial-part-i-hacking-with.html)
"""
doc.section "API"
doc.subsection "Creating streams"
doc.fn "$.asEventStream(eventName : String)", """
creates an EventStream from events on a
jQuery or Zepto.js object. You can pass optional arguments to add a
jQuery live selector and/or a function that processes the jQuery
event and its parameters, if given, like this:
```js
$("#my-div").asEventStream("click", ".more-specific-selector")
$("#my-div").asEventStream("click", ".more-specific-selector", function(event, args) { return args[0] })
$("#my-div").asEventStream("click", function(event, args) { return args[0] })
```
"""
doc.fn "Bacon.fromPromise(promise : Promise[A] [, abort : boolean][, eventTransformer]) : EventStream[A]", """
creates an EventStream from a Promise object such as JQuery Ajax.
This stream will contain a single value or an error, followed immediately by stream end.
You can use the optional abort flag (i.e. ´fromPromise(p, true)´ to have the `abort` method of the given promise be called when all subscribers have been removed from the created stream.
You can also pass an optional function that transforms the promise value into Events. The default is to transform the value into `[new Bacon.Next(value), new Bacon.End()]`.
Check out this [example](https://github.com/raimohanska/baconjs-examples/blob/master/resources/public/index.html).
"""
doc.fn "Bacon.fromEvent(target : EventTarget | EventEmitter, eventName : String [, eventTransformer]) : EventStream", """
creates an EventStream from events
on a DOM EventTarget or Node.JS EventEmitter object, or an object that supports event listeners using `on`/`off` methods.
You can also pass an optional function that transforms the emitted
events' parameters.
```js
Bacon.fromEvent(document.body, "click").onValue(function() { alert("Bacon!") })
```
"""
doc.fn "Bacon.fromCallback(f : (A -> void) -> void [, args...]) : EventStream[A]", """
creates an EventStream from a function that
accepts a callback. The function is supposed to call its callback just
once. For example:
```js
Bacon.fromCallback(function(callback) {
setTimeout(function() {
callback("Bacon!")
}, 1000)
})
```
This would create a stream that outputs a single value "Bacon!" and ends
after that. The use of setTimeout causes the value to be delayed by 1
second.
You can also give any number of arguments to [`fromCallback`](#bacon-fromcallback), which will be
passed to the function. These arguments can be simple variables, Bacon
EventStreams or Properties. For example the following will output "Bacon rules":
```js
bacon = Bacon.constant('bacon')
Bacon.fromCallback(function(a, b, callback) {
callback(a + ' ' + b);
}, bacon, 'rules').log();
```
"""
doc.fnOverload "Bacon.fromCallback(object, methodName [, args...]) : EventStream[A]", "object", """
a variant of fromCallback which calls the named method of a given object.
"""
doc.fn "Bacon.fromNodeCallback(f : (E -> A -> void) -> void [, args...]) : EventStream[A]", """
behaves the same way as [`Bacon.fromCallback`](#bacon-fromcallback),
except that it expects the callback to be called in the Node.js convention:
`callback(error, data)`, where error is null if everything is fine. For example:
```js
var Bacon = require('baconjs').Bacon,
fs = require('fs');
var read = Bacon.fromNodeCallback(fs.readFile, 'input.txt');
read.onError(function(error) { console.log("Reading failed: " + error); });
read.onValue(function(value) { console.log("Read contents: " + value); });
```
"""
doc.fnOverload "Bacon.fromNodeCallback(object, methodName [, args...])", "object", """
a variant of fromNodeCallback which calls the named method of a given object.
"""
doc.fn "Bacon.fromPoll(interval : Number, f : -> Event[A]) : EventStream[A]", """polls given function with given interval.
Function should return Events: either `Bacon.Next` or `Bacon.End`. Polling occurs only
when there are subscribers to the stream. Polling ends permanently when
`f` returns `Bacon.End`.
"""
doc.fn "Bacon.once(value : Event[A] | A) : EventStream[A]", """
creates an EventStream that delivers the given
single value for the first subscriber. The stream will end immediately
after this value. You can also send an `Bacon.Error` event instead of a
value: `Bacon.once(new Bacon.Error("fail"))`.
"""
doc.fn "Bacon.fromArray(values : Array[Event[A] | A]) : EventStream[A]", """
creates an EventStream that delivers the given
series of values (given as array) to the first subscriber. The stream ends after these
values have been delivered. You can also send `Bacon.Error` events, or
any combination of pure values and error events like this:
`Bacon.fromArray([1, new Bacon.Error()])
"""
doc.fn "Bacon.interval(interval : Number, value : A) : EventStream[A]", """
repeats the single element
indefinitely with the given interval (in milliseconds)
"""
doc.fn "Bacon.sequentially(interval : Number, values : Array[A]) : EventStream[A]", """
creates a stream containing given
values (given as array). Delivered with given interval in milliseconds.
"""
doc.fn "Bacon.repeatedly(interval : Number, values : Array[A]) : EventStream[A]", """
repeats given elements indefinitely
with given interval in milliseconds. For example, `repeatedly(10, [1,2,3])`
would lead to `1,2,3,1,2,3...` to be repeated indefinitely.
"""
doc.fn "Bacon.repeat(fn: Number -> Observable[A]): EventStream[A]", """
Calls generator function which is expected to return an observable. The returned EventStream contains
values and errors from the spawned observable. When the spawned observable ends, the generator is called
again to spawn a new observable.
This is repeated until the generator returns a falsy value
(such as `undefined` or `false`).
The generator function is called with one argument — iteration number starting from `0`.
Here's an example:
```js
Bacon.repeat(function(i) {
if (i < 3) {
return Bacon.once(i);
} else {
return false;
}
}).log()
```
The example will produce values 0, 1 and 2.
"""
doc.fn "Bacon.never() : EventStream", """
creates an EventStream that immediately ends.
"""
doc.fn "Bacon.later(delay : Number, value : A) : EventStream[A]", """
creates a single-element stream that
produces given value after given delay (milliseconds).
"""
doc.fn "new Bacon.EventStream(subscribe)", """
creates an `EventStream` with the given subscribe function.
"""
doc.text """
`property.changes` creates a stream of changes to the `Property`. The stream *does not* include
an event for the current value of the Property at the time this method was called.
"""
doc.fn "property.toEventStream(@ : Property[A]) : EventStream[A]", """creates an EventStream based on this Property. The stream contains also an event for the current
value of this Property at the time this method was called.
"""
doc.text """
[`new Bacon.Bus()`](#new-bacon-bus) creates a pushable/pluggable stream (see [Bus](#bus) section below)
Pro tip: you can also put Errors into streams created with the
constructors above, by using an [`Bacon.Error`](#bacon-error) object instead of a plain
value.
"""
doc.subsection "Bacon.fromBinder for custom streams"
doc.text """
If none of the factory methods above apply, you may of course roll your own EventStream by using `Bacon.fromBinder`.
"""
doc.fn "Bacon.fromBinder(subscribe)", """
The parameter `subscribe` is a function that accepts a `sink` which is a function that your `subscribe` function can "push" events to.
For example:
```js
var stream = Bacon.fromBinder(function(sink) {
sink("first value")
sink([new Bacon.Next("2nd"), new Bacon.Next("3rd")])
sink(new Bacon.Next(function() {
return "This one will be evaluated lazily"
}))
sink(new Bacon.Error("oops, an error"))
sink(new Bacon.End())
return function() {
// unsub functionality here, this one's a no-op
}
})
stream.log()
```
As shown in the example, you can push
- A plain value, like `"first value"`
- An `Event` object including `Bacon.Error` (wraps an error) and `Bacon.End` (indicates
stream end).
- An array of event objects at once
Other examples can be found on [JSFiddle](https://jsfiddle.net/PG4c4/) and the
[Bacon.js blog](https://baconjs.blogspot.fi/2013/12/wrapping-things-in-bacon.html).
The `subscribe` function must return a function. Let's call that function
`unsubscribe`. The returned function can be used by the subscriber (directly or indirectly) to
unsubscribe from the EventStream. It should release all resources that the subscribe function reserved.
The `sink` function may return [`Bacon.noMore`](#bacon-nomore) (as well as [`Bacon.more`](#bacon-more)
or any other value). If it returns [`Bacon.noMore`](#bacon-nomore), no further events will be consumed
by the subscriber. The `subscribe` function may choose to clean up all resources at this point (e.g.,
by calling `unsubscribe`). This is usually not necessary, because further calls to `sink` are ignored,
but doing so can increase performance in [rare cases](https://github.com/baconjs/bacon.js/issues/484).
The EventStream will wrap your `subscribe` function so that it will
only be called when the first stream listener is added, and the `unsubscribe`
function is called only after the last listener has been removed.
The subscribe-unsubscribe cycle may of course be repeated indefinitely,
so prepare for multiple calls to the subscribe function.
A note about the `new Bacon.Next(..)` constructor: You can use it like
```js
new Bacon.Next("value")
```
But the canonical way would be
```js
new Bacon.Next(function() { return "value"; })
```
The former version is safe only when you know that the actual value in
the stream is not a function.
The idea in using a function instead of a plain value is that the internals on Bacon.js take
advantage of [lazy evaluation](#lazy-evaluation) by deferring the evaluations of values
created by [`map`](#observable-map), [`combine`](#combining-multiple-streams-and-properties).
"""
doc.fn "Bacon.noMore", """The opaque value `sink` function may return. See `Bacon.fromBinder`."""
doc.fn "Bacon.more", """The opaque value `sink` function may return. See `Bacon.fromBinder`."""
doc.subsection "Common methods in EventStreams and Properties"
doc.text """
Both EventStream and Property share the Observable interface, and hence
share a lot of methods. Common methods are listed below.
"""
doc.fn "observable.onValue(@ : Observable[A], f : A -> void) : Unsubscriber", """
subscribes a given handler function to the observable. Function will be called for each new value.
This is the simplest way to assign a side-effect to an observable. The difference
to the `subscribe` method is that the actual stream values are
received, instead of `Event` objects.
`stream.onValue` and `property.onValue` behave similarly, except that the latter also
pushes the initial value of the property, in case there is one.
"""
doc.fn "observable.onError(@ : Observable[A], f : Error -> void) : Unsubscriber", """
subscribes a callback to error events. The function
will be called for each error in the stream.
"""
doc.fn "observable.onEnd(f : -> void) : Unsubscriber", """
subscribes a callback to stream end. The function will
be called when the stream ends. Just like `subscribe`, this method returns a function for unsubscribing.
"""
doc.fn "observable.toPromise(@ : Observable[A] [, PromiseCtr]) : Promise[A]", """
returns a Promise which will be resolved with the last event coming from an Observable.
The global ES6 promise implementation will be used unless a promise constructor is given.
Use a shim if you need to support legacy browsers or platforms.
[caniuse promises](https://caniuse.com/#feat=promises).
"""
doc.fn "observable.firstToPromise(@ : Observable[A] [, PromiseCtr]) : Promise[A]", """
returns a Promise which will be resolved with the first event coming from an Observable.
Like `toPromise`, the global ES6 promise implementation will be used unless a promise
constructor is given.
"""
doc.fn "observable.map(@ : Observable[A], f : A -> B) : Observable[B]", """
maps values using given function, returning a new
stream/property. Instead of a function, you can also provide a constant
value. Further, you can use a property extractor string like
".keyCode". So, if f is a string starting with a
dot, the elements will be mapped to the corresponding field/function in the event
value. For instance map(".keyCode") will pluck the keyCode field from
the input values. If keyCode was a function, the result stream would
contain the values returned by the function.
The [Function Construction rules](#function-construction-rules) below apply here.
The `map` method, among many others, uses [lazy evaluation](#lazy-evaluation).
"""
doc.fn "stream.map(property)", """
maps the stream events to the current value of
the given property. This is equivalent to `property.sampledBy(stream)`.
"""
doc.fn "observable.mapError(@ : Observable[A], f : E -> A) : Observable[A]", """
maps errors using given function. More
specifically, feeds the "error" field of the error event to the function
and produces a `Next` event based on the return value.
The [Function Construction rules](#function-construction-rules) below apply here.
You can omit the argument to produce a `Next` event with `undefined` value.
"""
doc.fn "observable.errors(@ : Observable[A]) : Observable[A]", """
returns a stream containing [`Error`](#bacon-error) events only.
Same as filtering with a function that always returns false.
"""
doc.fn "observable.skipErrors(@ : Observable[A]) : Observable[A]", """
skips all errors.
"""
doc.fn "observable.mapEnd(@ : Observable[A], f : -> Observable[A]) : Observable[A]", """
Adds an extra `Next` event just before End. The value is created
by calling the given function when the source stream ends. Instead of a
function, a static value can be used. You can omit the argument to
produce a Next event with `undefined` value.
"""
doc.fn "observable.filter(@ : Observable[A], f : A -> Bool) : Observable[A]", """
filters values using given predicate function.
Instead of a function, you can use a constant value (`true` to include all, `false` to exclude all) or a
property extractor string (like ".isValuable") instead. Just like with
[`map`](#observable-map), indeed.
"""
doc.fnOverload "observable.filter(property)", "property", """
filters values based on the value of a
property. Event will be included in output [if and only if](https://en.wikipedia.org/wiki/If_and_only_if) the property holds `true`
at the time of the event.
"""
doc.fn "observable.takeWhile(@ : Observable[A], f : A -> Bool) : Observable[A]", """
takes while given predicate function holds true, and then ends.
[Function Construction rules](#function-construction-rules) apply.
"""
doc.fnOverload "observable.takeWhile(property)", "property", """
takes values while the value of a property holds true, and then ends.
"""
doc.fn "observable.take(@ : Observable[A], n : Number) : Observable[A]", """
takes at most n elements from the stream.
Equal to `Bacon.never()` if `n <= 0`.
"""
doc.fn "observable.takeUntil(@ : Observable[A], stream : EventStream[B]) : Observable[A]", """
takes elements from source until a Next event
appears in the other stream. If other stream ends without value, it is
ignored
"""
doc.fn "observable.first(@ : Observable[A]) : Observable[A]", """
takes the first element from the stream. Essentially `observable.take(1)`.
"""
doc.fn "observable.last(@ : Observable[A]) : Observable[A]", """
takes the last element from the stream. None, if stream is empty.
*Note:* `neverEndingStream.last()` creates the stream which doesn't produce any events and never ends.
"""
doc.fn "observable.skip(n)", """
skips the first n elements from the stream
"""
doc.fn "observable.delay(delay)", """
delays the stream/property by given amount of milliseconds. Does not delay the initial value of a `Property`.
```js
var delayed = source.delay(2)
```
```
source: asdf----asdf----
delayed: --asdf----asdf--
```
"""
doc.fn "observable.throttle(delay)", """
throttles stream/property by given amount
of milliseconds. Events are emitted with the minimum interval of
`delay`. The implementation is based on `stream.bufferWithTime`.
Does not affect emitting the initial value of a `Property`.
Example:
```js
var throttled = source.throttle(2)
```
```
source: asdf----asdf----
throttled: --s--f----s--f--
```
"""
doc.fn "observable.debounce(delay)", """
throttles stream/property by given amount
of milliseconds, but so that event is only emitted after the given
"quiet period". Does not affect emitting the initial value of a Property.
The difference of `throttle` and `debounce` is the same as it is in the
same methods in jQuery.
Example:
```
source: asdf----asdf----
source.debounce(2): -----f-------f--
```
"""
doc.fn "observable.debounceImmediate(delay)", """
passes the first event in the
stream through, but after that, only passes events after a given number
of milliseconds have passed since previous output.
Example:
```
source: asdf----asdf----
source.debounceImmediate(2): a-d-----a-d-----
```
"""
doc.fn "observable.bufferingThrottle(@ : Observable[A], minimumInterval) : EventStream[A]", """
throttles the observable using a buffer so that at most one value event in minimumInteval is issued.
Unlike `throttle`, it doesn't discard the excessive events but buffers them instead, outputting
them with a rate of at most one value per minimumInterval.
Example:
```js
var throttled = source.bufferingThrottle(2)
```
```
source: asdf----asdf----
throttled: a-s-d-f-a-s-d-f-
```
"""
doc.fn "observable.doAction(f)", """
returns a stream/property where the function f
is executed for each value, before dispatching to subscribers. This is
useful for debugging, but also for stuff like calling the
`preventDefault()` method for events. In fact, you can
also use a property-extractor string instead of a function, as in
`".preventDefault"`.
"""
doc.fn "observable.doError(f)", """
returns a stream/property where the function f
is executed for each error, before dispatching to subscribers.
That is, same as `doAction` but for errors.
"""
doc.fn "observable.not(@ : Obserable[A]) : Observable[Bool]", """
returns a stream/property that inverts boolean values
"""
doc.fn "observable.flatMap(@ : Observable[A], f : A -> Observable[B] | Event[B] | B) : EventStream[B]", """
for each element in the source stream, spawn a new
stream using the function `f`. Collect events from each of the spawned
streams into the result `EventStream`. Note that instead of a function, you can provide a
stream/property too. Also, the return value of function `f` can be either an
`Observable` (stream/property) or a constant value. The result of
`flatMap` is always an `EventStream`.
The [Function Construction rules](#function-construction-rules) below apply here.
`stream.flatMap()` can be used conveniently with `Bacon.once()` and `Bacon.never()` for converting and filtering at the same time, including only some of the results.
Example - converting strings to integers, skipping empty values:
```js
stream.flatMap(function(text) {
return (text != "") ? parseInt(text) : Bacon.never()
})
```
"""
doc.fn "observable.flatMapLatest(f)", """
like `flatMap`, but instead of including events from
all spawned streams, only includes them from the latest spawned stream.
You can think this as switching from stream to stream.
Note that instead of a function, you can provide a stream/property too.
The [Function Construction rules](#function-construction-rules) below apply here.
"""
doc.fn "observable.flatMapFirst(f)", """
like `flatMap`, but only spawns a new
stream if the previously spawned stream has ended.
The [Function Construction rules](#function-construction-rules) below apply here.
"""
doc.fn "observable.flatMapError(f)", """
like `flatMap`, but is applied only on [`Error`](#bacon-error) events. Returned values go into the
value stream, unless an error event is returned. As an example, one type of error could result in a retry and another just
passed through, which can be implemented using flatMapError.
"""
doc.fn "observable.flatMapWithConcurrencyLimit(@ : Observable[A], limit : Number, f : A -> Observable[B] | Event[B] | B) : EventStream[B]", """
a super method of *flatMap* family. It limits the number of open spawned streams and buffers incoming events.
`flatMapConcat` is `flatMapWithConcurrencyLimit(1)` (only one input active),
and `flatMap` is `flatMapWithConcurrencyLimit ∞` (all inputs are piped to output).
The [Function Construction rules](#function-construction-rules) below apply here.
"""
doc.fn "observable.flatMapConcat(@ : Observable[A], f : A -> Observable[B] | Event[B] | B) : EventStream[B]", """
a `flatMapWithConcurrencyLimit` with limit of 1.
The [Function Construction rules](#function-construction-rules) below apply here.
"""
doc.fn "observable.scan(seed, f) : Property[A]", """
scans stream/property with given seed value and
accumulator function, resulting to a Property. For example, you might
use zero as seed and a "plus" function as the accumulator to create
an "integral" property. Instead of a function, you can also supply a
method name such as ".concat", in which case this method is called on
the accumulator value and the new stream value is used as argument.
Example:
```js
var plus = function (a,b) { return a + b }
Bacon.sequentially(1, [1,2,3]).scan(0, plus)
```
This would result to following elements in the result stream:
seed value = 0
0 + 1 = 1
1 + 2 = 3
3 + 3 = 6
When applied to a Property as in `r = p.scan(seed, f)`, there's a (hopefully insignificant) catch:
The starting value for `r` depends on whether `p` has an
initial value when scan is applied. If there's no initial value, this works
identically to EventStream.scan: the `seed` will be the initial value of
`r`. However, if `r` already has a current/initial value `x`, the
seed won't be output as is. Instead, the initial value of `r` will be `f(seed, x)`. This makes sense,
because there can only be 1 initial value for a Property at a time.
"""
doc.fn "observable.fold(seed, f) : Property[A]", """
is like `scan` but only emits the final
value, i.e. the value just before the observable ends. Returns a
`Property`.
"""
doc.fn "observable.reduce(seed,f)", "synonym for `fold`."
doc.fn "observable.diff(start, f)", """
returns a Property that represents the result of a comparison
between the previous and current value of the Observable. For the initial value of the Observable,
the previous value will be the given start.
Example:
```js
var distance = function (a,b) { return Math.abs(b - a) }
Bacon.sequentially(1, [1,2,3]).diff(0, distance)
```
This would result to following elements in the result stream:
1 - 0 = 1
2 - 1 = 1
3 - 2 = 1
"""
doc.fn "observable.zip(other, f)", """
return an EventStream with elements
pair-wise lined up with events from this and the other stream.
A zipped stream will publish only when it has a value from each
stream and will only produce values up to when any single stream ends.
Be careful not to have too much "drift" between streams. If one stream
produces many more values than some other excessive buffering will
occur inside the zipped observable.
Example 1:
```js
var x = Bacon.fromArray([1, 2])
var y = Bacon.fromArray([3, 4])
x.zip(y, function(x, y) { return x + y })
# produces values 4, 6
```
Example 2:
You can use zip to combine observables that are pairwise synchronized
from e.g. projections or sampling by the same property, while avoiding
the double-processing that would happen recombining with `combine`.
```js
var x = obs.map('.x')
var y = obs.map('.y')
x.zip(y, makeComplex)
```
"""
doc.fn "observable.slidingWindow(max[, min])", """
returns a Property that represents a
"sliding window" into the history of the values of the Observable. The
result Property will have a value that is an array containing the last `n`
values of the original observable, where `n` is at most the value of the
`max` argument, and at least the value of the `min` argument. If the
`min` argument is omitted, there's no lower limit of values.
For example, if you have a stream `s` with value a sequence 1 - 2 - 3 - 4 - 5, the
respective values in `s.slidingWindow(2)` would be [] - [1] - [1,2] -
[2,3] - [3,4] - [4,5]. The values of `s.slidingWindow(2,2)`would be
[1,2] - [2,3] - [3,4] - [4,5].
"""
doc.fn "observable.log()", """
logs each value of the Observable to the console.
It optionally takes arguments to pass to console.log() alongside each
value. To assist with chaining, it returns the original Observable. Note
that as a side-effect, the observable will have a constant listener and
will not be garbage-collected. So, use this for debugging only and
remove from production code. For example:
```js
myStream.log("New event in myStream")
```
or just
```js
myStream.log()
```
"""
doc.fn "observable.doLog()", """
logs each value of the Observable to the console. doLog() behaves like `log`
but does not subscribe to the event stream. You can think of doLog() as a
logger function that – unlike log() – is safe to use in production. doLog() is
safe, because it does not cause the same surprising side-effects as log()
does.
"""
doc.fn "observable.combine(property2, f)", """
combines the latest values of the two
streams or properties using a two-arg function. Similarly to `scan`, you can use a
method name instead, so you could do `a.combine(b, ".concat")` for two
properties with array value. The result is a Property.
"""
doc.fn "observable.withStateMachine(initState, f)", """
lets you run a state machine
on an observable. Give it an initial state object and a state
transformation function that processes each incoming event and
returns and array containing the next state and an array of output
events. Here's an an example, where we calculate the total sum of all
numbers in the stream and output the value on stream end:
```js
Bacon.fromArray([1,2,3])
.withStateMachine(0, function(sum, event) {
if (event.hasValue())
return [sum + event.value(), []]
else if (event.isEnd())
return [undefined, [new Bacon.Next(sum), event]]
else
return [sum, [event]]
})
```
"""
doc.fn "observable.decode(mapping)", """
decodes input using the given mapping. Is a
bit like a switch-case or the decode function in Oracle SQL. For
example, the following would map the value 1 into the string "mike"
and the value 2 into the value of the `who` property.
```js
property.decode({1 : "mike", 2 : who})
```
This is actually based on `combineTemplate` so you can compose static
and dynamic data quite freely, as in
```js
property.decode({1 : { type: "mike" }, 2 : { type: "other", whoThen : who }})
```
The return value of `decode` is always a `Property`.
"""
doc.fn "observable.awaiting(otherObservable)", """
creates a Property that indicates whether
`observable` is awaiting `otherObservable`, i.e. has produced a value after the latest
value from `otherObservable`. This is handy for keeping track whether we are
currently awaiting an AJAX response:
```js
var showAjaxIndicator = ajaxRequest.awaiting(ajaxResponse)
```
"""
doc.fn "observable.endOnError()", """
ends the `Observable` on first [`Error`](#bacon-error) event. The
error is included in the output of the returned `Observable`.
"""
doc.fnOverload "observable.endOnError(f)", "f", """
ends the `Observable` on first [`Error`](#bacon-error) event for which
the given predicate function returns true. The error is included in the
output of the returned `Observable`. The [Function Construction rules](#function-construction-rules) apply, so
you can do for example `.endOnError(".serious")`.
"""
doc.fn "observable.withHandler(f)", """
lets you do more custom event handling: you
get all events to your function and you can output any number of events
and end the stream if you choose. For example, to send an error and end
the stream in case a value is below zero:
```js
if (event.hasValue() && event.value() < 0) {
this.push(new Bacon.Error("Value below zero"));
return this.push(end());
} else {
return this.push(event);
}
```
Note that it's important to return the value from `this.push` so that
the connection to the underlying stream will be closed when no more
events are needed.
"""
doc.fn "observable.name(@ : Observable[A], newName : String) : Observable[A]", """
sets the name of the observable. Overrides the default
implementation of `toString` and `inspect`.
Returns itself.
"""
doc.fn "observable.withDescription(@ : Observable[A], param...) : Observable[A]", """
Sets the structured description of the observable. The `toString` and `inspect` methods
use this data recursively to create a string representation for the observable. This method
is probably useful for Bacon core / library / plugin development only.
For example:
var src = Bacon.once(1)
var obs = src.map(function(x) { return -x })
console.log(obs.toString())
--> Bacon.once(1).map(function)
obs.withDescription(src, "times", -1)
console.log(obs.toString())
--> Bacon.once(1).times(-1)
"""
doc.fn "observable.groupBy(@ : Observable[A], keyF[, limitF]) : Observable[Observable[A]]", """
Groups stream events to new streams by `keyF`. Optional `limitF` can be provided to limit grouped
stream life. Stream transformed by `limitF` is passed on if provided. `limitF` gets grouped stream
and the original event causing the stream to start as parameters.
Calculator for grouped consecutive values until group is cancelled:
var events = [
{id: 1, type: "add", val: 3 },
{id: 2, type: "add", val: -1 },
{id: 1, type: "add", val: 2 },
{id: 2, type: "cancel"},
{id: 3, type: "add", val: 2 },
{id: 3, type: "cancel"},
{id: 1, type: "add", val: 1 },
{id: 1, type: "add", val: 2 },
{id: 1, type: "cancel"}
]
function keyF(event) {
return event.id
}
function limitF(groupedStream, groupStartingEvent) {
var cancel = groupedStream.filter(function(x) { return x.type === "cancel"}).take(1)
var adds = groupedStream.filter(function(x) { return x.type === "add" })
adds.takeUntil(cancel).map(".val")
}
Bacon.sequentially(2, events)
.groupBy(keyF, limitF)
.flatMap(function(groupedStream) {
return groupedStream.fold(0, function(acc, x) { return acc + x })
})
.onValue(function(sum) {
console.log(sum)
// returns [-1, 2, 8] in an order
})
"""
doc.subsection "EventStream"
doc.fn "Bacon.EventStream", "a stream of events. See methods below."
doc.fn "stream.subscribe(f)", """
subscribes given handler function to
event stream. Function will receive Event objects (see below).
The subscribe() call returns a `unsubscribe` function that you can
call to unsubscribe. You can also unsubscribe by returning
`Bacon.noMore` from the handler function as a reply to an Event.
"""
doc.fn "stream.onValue(f)", """
subscribes a given handler function to event
stream. Function will be called for each new value in the stream. This
is the simplest way to assign a side-effect to a stream. The difference
to the `subscribe` method is that the actual stream values are
received, instead of `Event` objects.
The [Function Construction rules](#function-construction-rules) below apply here.
Just like `subscribe`, this method returns a function for unsubscribing.
"""
doc.fn "stream.onValues(f)", """
like [`onValue`](#stream-onvalue), but splits the value (assuming its an
array) as function arguments to `f`.
"""
doc.fn "stream.skipDuplicates([isEqual])", """
drops consecutive equal elements. So,
from `[1, 2, 2, 1]` you'd get `[1, 2, 1]`. Uses the `===` operator for equality
checking by default. If the isEqual argument is supplied, checks by calling