rxjs
Version:
Reactive Extensions for modern JavaScript
244 lines (230 loc) • 8.04 kB
text/typescript
/** @prettier */
import { isArrayLike } from '../util/isArrayLike';
import { isPromise } from '../util/isPromise';
import { iterator as Symbol_iterator } from '../symbol/iterator';
import { observable as Symbol_observable } from '../symbol/observable';
import { Subscriber } from '../Subscriber';
import { Observable } from '../Observable';
import { ObservableInput, SchedulerLike, ObservedValueOf } from '../types';
import { scheduled } from '../scheduled/scheduled';
import { isFunction } from '../util/isFunction';
import { reportUnhandledError } from '../util/reportUnhandledError';
import { isInteropObservable } from '../util/isInteropObservable';
import { isAsyncIterable } from '../util/isAsyncIterable';
import { createInvalidObservableTypeError } from '../util/throwUnobservableError';
import { isIterable } from '../util/isIterable';
export function from<O extends ObservableInput<any>>(input: O): Observable<ObservedValueOf<O>>;
/** @deprecated The scheduler argument is deprecated, use scheduled. Details: https://rxjs.dev/deprecations/scheduler-argument */
export function from<O extends ObservableInput<any>>(input: O, scheduler: SchedulerLike): Observable<ObservedValueOf<O>>;
/**
* Creates an Observable from an Array, an array-like object, a Promise, an iterable object, or an Observable-like object.
*
* <span class="informal">Converts almost anything to an Observable.</span>
*
* 
*
* `from` converts various other objects and data types into Observables. It also converts a Promise, an array-like, or an
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterable" target="_blank">iterable</a>
* object into an Observable that emits the items in that promise, array, or iterable. A String, in this context, is treated
* as an array of characters. Observable-like objects (contains a function named with the ES2015 Symbol for Observable) can also be
* converted through this operator.
*
* ## Examples
*
* ### Converts an array to an Observable
*
* ```ts
* import { from } from 'rxjs';
*
* const array = [10, 20, 30];
* const result = from(array);
*
* result.subscribe(x => console.log(x));
*
* // Logs:
* // 10
* // 20
* // 30
* ```
*
* ---
*
* ### Convert an infinite iterable (from a generator) to an Observable
*
* ```ts
* import { from } from 'rxjs';
* import { take } from 'rxjs/operators';
*
* function* generateDoubles(seed) {
* let i = seed;
* while (true) {
* yield i;
* i = 2 * i; // double it
* }
* }
*
* const iterator = generateDoubles(3);
* const result = from(iterator).pipe(take(10));
*
* result.subscribe(x => console.log(x));
*
* // Logs:
* // 3
* // 6
* // 12
* // 24
* // 48
* // 96
* // 192
* // 384
* // 768
* // 1536
* ```
*
* ---
*
* ### With async scheduler
*
* ```ts
* import { from, asyncScheduler } from 'rxjs';
*
* console.log('start');
*
* const array = [10, 20, 30];
* const result = from(array, asyncScheduler);
*
* result.subscribe(x => console.log(x));
*
* console.log('end');
*
* // Logs:
* // start
* // end
* // 10
* // 20
* // 30
* ```
*
* @see {@link fromEvent}
* @see {@link fromEventPattern}
*
* @param {ObservableInput<T>} A subscription object, a Promise, an Observable-like,
* an Array, an iterable, or an array-like object to be converted.
* @param {SchedulerLike} An optional {@link SchedulerLike} on which to schedule the emission of values.
* @return {Observable<T>}
*/
export function from<T>(input: ObservableInput<T>, scheduler?: SchedulerLike): Observable<T> {
return scheduler ? scheduled(input, scheduler) : innerFrom(input);
}
// TODO: Use this throughout the library, rather than the `from` above, to avoid
// the unnecessary scheduling check and reduce bundled sizes of operators that use `from`.
// TODO: Eventually, this just becomes `from`, as we don't have the deprecated scheduled path anymore.
export function innerFrom<T>(input: ObservableInput<T>): Observable<T> {
if (input instanceof Observable) {
return input;
}
if (input != null) {
if (isInteropObservable(input)) {
return fromInteropObservable(input);
}
if (isArrayLike(input)) {
return fromArrayLike(input);
}
if (isPromise(input)) {
return fromPromise(input);
}
if (isAsyncIterable(input)) {
return fromAsyncIterable(input);
}
if (isIterable(input)) {
return fromIterable(input);
}
}
throw createInvalidObservableTypeError(input);
}
/**
* Creates an RxJS Observable from an object that implements `Symbol.observable`.
* @param obj An object that properly implements `Symbol.observable`.
*/
function fromInteropObservable<T>(obj: any) {
return new Observable((subscriber: Subscriber<T>) => {
const obs = obj[Symbol_observable]();
if (isFunction(obs.subscribe)) {
return obs.subscribe(subscriber);
}
// Should be caught by observable subscribe function error handling.
throw new TypeError('Provided object does not correctly implement Symbol.observable');
});
}
/**
* Synchronously emits the values of an array like and completes.
* This is exported because there are creation functions and operators that need to
* make direct use of the same logic, and there's no reason to make them run through
* `from` conditionals because we *know* they're dealing with an array.
* @param array The array to emit values from
*/
export function fromArrayLike<T>(array: ArrayLike<T>) {
return new Observable((subscriber: Subscriber<T>) => {
// Loop over the array and emit each value. Note two things here:
// 1. We're making sure that the subscriber is not closed on each loop.
// This is so we don't continue looping over a very large array after
// something like a `take`, `takeWhile`, or other synchronous unsubscription
// has already unsubscribed.
// 2. In this form, reentrant code can alter that array we're looping over.
// This is a known issue, but considered an edge case. The alternative would
// be to copy the array before executing the loop, but this has
// performance implications.
for (let i = 0; i < array.length && !subscriber.closed; i++) {
subscriber.next(array[i]);
}
subscriber.complete();
});
}
function fromPromise<T>(promise: PromiseLike<T>) {
return new Observable((subscriber: Subscriber<T>) => {
promise
.then(
(value) => {
if (!subscriber.closed) {
subscriber.next(value);
subscriber.complete();
}
},
(err: any) => subscriber.error(err)
)
.then(null, reportUnhandledError);
});
}
function fromIterable<T>(iterable: Iterable<T>) {
return new Observable((subscriber: Subscriber<T>) => {
const iterator = (iterable as any)[Symbol_iterator]();
while (!subscriber.closed) {
// Note that any error thrown in the iterator here
// will be caught by the subscribe call in `Observable`,
// and sent to the consumer via `subscriber.error`.
const { done, value } = iterator.next();
if (done) {
// If we're done, just complete. This will set
// subscriber.closed to `true` and kill the loop.
// We don't next values from an "done" iterator result.
// This is to mirror the behavior of JavaScripts `for..of`.
subscriber.complete();
} else {
subscriber.next(value);
}
}
// Finalize the iterator if it happens to be a Generator
return () => isFunction(iterator?.return) && iterator.return();
});
}
function fromAsyncIterable<T>(asyncIterable: AsyncIterable<T>) {
return new Observable((subscriber: Subscriber<T>) => {
process(asyncIterable, subscriber).catch((err) => subscriber.error(err));
});
}
async function process<T>(asyncIterable: AsyncIterable<T>, subscriber: Subscriber<T>) {
for await (const value of asyncIterable) {
subscriber.next(value);
}
subscriber.complete();
}