An Implementation of Observables for Javascript

Overview

zen-observable

An implementation of Observables for JavaScript. Requires Promises or a Promise polyfill.

Install

npm install zen-observable

Usage

import Observable from 'zen-observable';

Observable.of(1, 2, 3).subscribe(x => console.log(x));

API

new Observable(subscribe)

let observable = new Observable(observer => {
  // Emit a single value after 1 second
  let timer = setTimeout(() => {
    observer.next('hello');
    observer.complete();
  }, 1000);

  // On unsubscription, cancel the timer
  return () => clearTimeout(timer);
});

Creates a new Observable object using the specified subscriber function. The subscriber function is called whenever the subscribe method of the observable object is invoked. The subscriber function is passed an observer object which has the following methods:

  • next(value) Sends the next value in the sequence.
  • error(exception) Terminates the sequence with an exception.
  • complete() Terminates the sequence successfully.
  • closed A boolean property whose value is true if the observer's subscription is closed.

The subscriber function can optionally return either a cleanup function or a subscription object. If it returns a cleanup function, that function will be called when the subscription has closed. If it returns a subscription object, then the subscription's unsubscribe method will be invoked when the subscription has closed.

Observable.of(...items)

// Logs 1, 2, 3
Observable.of(1, 2, 3).subscribe(x => {
  console.log(x);
});

Returns an observable which will emit each supplied argument.

Observable.from(value)

let list = [1, 2, 3];

// Iterate over an object
Observable.from(list).subscribe(x => {
  console.log(x);
});
// Convert something 'observable' to an Observable instance
Observable.from(otherObservable).subscribe(x => {
  console.log(x);
});

Converts value to an Observable.

  • If value is an implementation of Observable, then it is converted to an instance of Observable as defined by this library.
  • Otherwise, it is converted to an Observable which synchronously iterates over value.

observable.subscribe([observer])

let subscription = observable.subscribe({
  next(x) { console.log(x) },
  error(err) { console.log(`Finished with error: ${ err }`) },
  complete() { console.log('Finished') }
});

Subscribes to the observable. Observer objects may have any of the following methods:

  • next(value) Receives the next value of the sequence.
  • error(exception) Receives the terminating error of the sequence.
  • complete() Called when the stream has completed successfully.

Returns a subscription object that can be used to cancel the stream.

observable.subscribe(nextCallback[, errorCallback, completeCallback])

let subscription = observable.subscribe(
  x => console.log(x),
  err => console.log(`Finished with error: ${ err }`),
  () => console.log('Finished')
);

Subscribes to the observable with callback functions. Returns a subscription object that can be used to cancel the stream.

observable.forEach(callback)

observable.forEach(x => {
  console.log(`Received value: ${ x }`);
}).then(() => {
  console.log('Finished successfully')
}).catch(err => {
  console.log(`Finished with error: ${ err }`);
})

Subscribes to the observable and returns a Promise for the completion value of the stream. The callback argument is called once for each value in the stream.

observable.filter(callback)

Observable.of(1, 2, 3).filter(value => {
  return value > 2;
}).subscribe(value => {
  console.log(value);
});
// 3

Returns a new Observable that emits all values which pass the test implemented by the callback argument.

observable.map(callback)

Returns a new Observable that emits the results of calling the callback argument for every value in the stream.

Observable.of(1, 2, 3).map(value => {
  return value * 2;
}).subscribe(value => {
  console.log(value);
});
// 2
// 4
// 6

observable.reduce(callback [,initialValue])

Observable.of(0, 1, 2, 3, 4).reduce((previousValue, currentValue) => {
  return previousValue + currentValue;
}).subscribe(result => {
  console.log(result);
});
// 10

Returns a new Observable that applies a function against an accumulator and each value of the stream to reduce it to a single value.

observable.concat(...sources)

Observable.of(1, 2, 3).concat(
  Observable.of(4, 5, 6),
  Observable.of(7, 8, 9)
).subscribe(result => {
  console.log(result);
});
// 1, 2, 3, 4, 5, 6, 7, 8, 9

Merges the current observable with additional observables.

Comments
  • Add ability to use Observables with async/await

    Add ability to use Observables with async/await

    A lot of modern JavaScript code uses async/await to handle asynchronous code. This is a great way to write code that is easy to read and understand. However, it is not possible to use async/await with Observables outside of the forEach() which requries more boilerplate than needed in order to do so.

    Changes:

    • Adds .all() to an observable instance allowing one to await the values it contains
    • Adds a test for the new functionality
    opened by nyteshade 19
  • ES module import is incompatible with Typings and should not be default

    ES module import is incompatible with Typings and should not be default

    The current manner of ES imports is incompatible with the Node and Browser imports and @types/zen-observable

    This manner of import should be supported and recommended.

    import * as Observable from 'zen-observable';
    
    opened by evans 11
  • Use `Symbol.for` to create `Symbol.observable` if it does not exist.

    Use `Symbol.for` to create `Symbol.observable` if it does not exist.

    This is the method used by symbol-observable and other packages (including RxJs/Observables).

    The current usage is causing false negatives from the is-observable package in latest Node.

    opened by jamestalmage 10
  • .then doesn't seem to work

    .then doesn't seem to work

    Running the following example from the README, then seems to be missing or not implemented.

    Observable.of(0, 1, 2, 3, 4).reduce((previousValue, currentValue) => {
        return previousValue + currentValue;
    }).then(result => {
        assert(result === 10);
    });
    
    opened by marcoscaceres 9
  • Throwing an exception from within the forEach callback should cancel the subscription

    Throwing an exception from within the forEach callback should cancel the subscription

    Here is what I was trying to do:

    "use strict";
    
    const Observable = require("zen-observable");
    
    Observable.of(1, 2, 3)
        .forEach(n => {
            console.log(n);
    
            // Since .forEach returns a promise I assumed that I should be
            // able to return it from here so that errors are propagated up.
            return Observable.of(4, 5, 6)
                .forEach(m => {
                    console.log(m);
                    throw new Error("ERROR!");
                });
        })
        .then(_ => console.log("completed."))
        .catch(err => console.error(err.stack));
    

    Here is the actual result:

    1          
    2          
    3          
    completed. 
    4          
    5          ! ---------
    6          !
    4          !
    5          !   How come both the forEach's survived an exception?
    6          !
    4          !
    5          !
    6          ! ---------
    

    Observations:

    • Errors are silently ignored.
    • "completed." is logged before the inner list has completed.
    • 5 and 6 get logged to the output... I had expected the 'throw' statement to abort the forEach().

    What is the best way to handle such a use case with this proposal?

    opened by kruncher 8
  • Create .mjs entry points for module bundlers supporting ESM.

    Create .mjs entry points for module bundlers supporting ESM.

    In modern bundlers (ex: Webpack and Rollup), .mjs have a higher priority than .js. This change will make those load ES modules instead of the commonjs versions, so the code can be treeshaked and hoisted.

    This will bypass the Babel build, but in general bundlers will use a compiler to support the ES* features needed.

    opened by BenoitZugmeyer 7
  • Observable.map() should run once then fan-out

    Observable.map() should run once then fan-out

    The map(callback) is called for each child subscription. That does not work for byte streams that can only be read once (Fetch API's response.json() for example can only be called once). Therefore the child observable should run the callback once and fan-out the result.

    I've implemented it here: https://github.com/RickWong/fetch-observable/blob/master/src%2Flib%2FBetterObservable.js#L15 (this map() implementation also supports resolving Promises)

    opened by RickWong 7
  • Would you like to use a transpiler?

    Would you like to use a transpiler?

    I have it as a dev dependency an I see it failing in older browsers at least because of "let" statement. Would you like to support any browser?

    opened by kof 6
  • Adjusts default.js to be in line with docs, and adds package fields.

    Adjusts default.js to be in line with docs, and adds package fields.

    I'm using ES2015 modules (particularly with rollup) a lot for my personal projects these days. zen-observable almost works, but there were a couple of snags which this PR addresses. The docs suggest what this module can be required in using:

    import Observable from "zen-observable";
    

    but with default.js and the package file as they are in master this line needs to be:

    import { Observable } from "zen-observable/default.js";
    

    This PR adjusts default.js and adds a module field to the package file for rollup to pick up (and I'm told webpack too). This field is to ES2015 modules as the main field is to Node style CommonJS modules. These changes bring the behaviour of this module into line with the docs without affecting internal modules.

    opened by qubyte 6
  • Fix crash on `ctor[Symbol.species]` not set

    Fix crash on `ctor[Symbol.species]` not set

    Hey there!

    I just encountered a problem. ctor[Symbol.species] seems to be undefined in one of my use cases and I really can't tell why. Everything else seems to be alright, though.

    So if it's possible that there is no Symbol.species set I propose a fallback for that case.

    Objections?

    Thx in advance!

    opened by andywer 5
  • Boxed/Static call needed?

    Boxed/Static call needed?

    If I do, for example, the following, nothing happens:

    var obs = new Observable(observe => {/*... do set up here*/});
    obs
      .filter(value => !value || !value.startsWith("//"))
      .reduce((collector,value) => collector.add(value), new Set())
      .forEach(set => console.log(set))
    

    However, if I do:

    var obs = new Observable(observe => {/*... do set up here*/});
    Observable.from(obs)
      .filter(value => !value || !value.startsWith("//"))
      .reduce((collector,value) => collector.add(value), new Set())
      .forEach(set => console.log(set))
    

    It works. However, that's obviously not ideal.

    opened by marcoscaceres 5
Owner
Kevin Smith
Kevin Smith
A promise library for JavaScript

If a function cannot return a value or throw an exception without blocking, it can return a promise instead. A promise is an object that represents th

Kris Kowal 15k Dec 30, 2022
Composable Concurrency Abstractions for JavaScript. :railway_track: :rhinoceros: :train: :vertical_traffic_light:

sporadic Composable Concurrency Abstractions for JavaScript. Example #!/usr/bin/env node (async () => { const sporadic = require('sporadic') cons

Marco Aurélio da Silva 16 Sep 24, 2022
CSP channels for Javascript (like Clojurescript's core.async, or Go) THIS IS AN UPSTREAM FORK

js-csp Communicating sequential processes for Javascript (like Clojurescript core.async, or Go). Examples var csp = require("js-csp"); Pingpong (porte

James Long 283 Sep 22, 2022
Highly performant JavaScript data stream ETL engine.

bellboy Highly performant JavaScript data stream ETL engine. How it works? Bellboy streams input data row by row. Every row, in turn, goes through use

Claviz 86 Dec 16, 2022
pattern matching in javascript & typescript made easy

?? matchbook pattern matching in typescript & javascript made easy matchbook is a lightweight & easy to use pattern matching library, for TypeScript a

@matchbook/ts 25 Dec 1, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Create DOM element and bind observables on it.

rx-domh Create DOM element and bind observables on it. Inspired by Binding.scala and react-flyd, I made this. Just a simple todo example: /** @jsx h *

xialvjun 4 Feb 6, 2018
See through the observables.

RxJS Insights RxJS Insights is a toolset that helps you debug the RxJS Observables. ✨ Developer friendly: Easy to setup. Easy to use. ✨ Comprehensive:

ksz ksz 319 Dec 30, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
This is a template project demonstrating how the MERN stack(Mongo, Express, React, Node) can be used, here we have the back end implementation and there is the React implementation as the front end

Versão em português MERN stack This is a template project demonstrating how the MERN stack(Mongo, Express, React, Node) can be used, here we have the

Douglas Samuel Gonçalves 2 Jan 22, 2022
Simple Library implemented using HTML, CSS and JavaScript. This is a simple implementation of JavaScript Modules of ES6.

Awesome-books A single page project with the porpuse of storing books' titles and authors. Built With CSS, HTML & Javascript. How to run in your local

Saadat Ali 7 Feb 21, 2022
HTML5 Canvas Gauge. Tiny implementation of highly configurable gauge using pure JavaScript and HTML5 canvas. No dependencies. Suitable for IoT devices because of minimum code base.

HTML Canvas Gauges v2.1 Installation Documentation Add-Ons Special Thanks License This is tiny implementation of highly configurable gauge using pure

Mykhailo Stadnyk 1.5k Dec 30, 2022
An arbitrary size Bit-Vector implementation in JavaScript

BitSet.js BitSet.js is an infinite Bit-Array (aka bit vector, bit string, bit set) implementation in JavaScript. That means that if you invert a bit v

Robert Eisele 207 Dec 9, 2022
sprintf.js is a complete open source JavaScript sprintf implementation

sprintf-js sprintf-js is a complete open source JavaScript sprintf implementation for the browser and Node.js. Note: as of v1.1.1 you might need some

Alexandru Mărășteanu 2k Jan 4, 2023
Use CSS-in-JavaScript with themes for React without being tightly coupled to one implementation

react-with-styles Use CSS-in-JavaScript for your React components without being tightly coupled to one implementation (e.g. Aphrodite, Radium, or Reac

Airbnb 1.7k Dec 8, 2022
Pure CSS (no JavaScript) implementation of Android Material design "ripple" animation

Pure CSS ripple effect (no JavaScript) CSS-only implementation of Android Material design "ripple" animation on click event Main advantage of this sol

Mladen Plavsic 334 Dec 11, 2022
A pure JavaScript implementation of git for node and browsers!

isomorphic-git isomorphic-git is a pure JavaScript reimplementation of git that works in both Node.js and browser JavaScript environments. It can read

isomorphic-git 6.7k Jan 4, 2023
IPFS implementation in JavaScript

The JavaScript implementation of the IPFS protocol Upgrading from <=0.40 to 0.48? See the release notes for the list of API changes and the migration

IPFS 7.2k Jan 8, 2023
A JavaScript implementation of Git.

JS-Git This project is a collection of modules that helps in implementing git powered applications in JavaScript. The original purpose for this is to

Tim Caswell 3.8k Dec 31, 2022