skip to content

In RxJS, what is the difference between of([1, 2, 3]) and from([1, 2, 3]), and what inputs does from() accept?

level: juniorimportance: must knowfreq 70%

answer

  1. arguments versus contents
  2. one array value or three numbers
  3. ObservableInput
  4. promises, iterables, streams

basics

~20 s

of([1, 2, 3]) emits the array as one value; from([1, 2, 3]) emits 1, 2 and 3 separately. from() converts any ObservableInput - arrays, iterables, promises, async iterables, ReadableStreams or Observables - into an Observable.

solid answer

~40 s

`of(...values)` emits each **argument** as-is, then completes, so `of([1, 2, 3])` emits a single array value and `of(1, 2, 3)` emits three numbers. `from(input)` **unpacks** one input: `from([1, 2, 3])` emits 1, 2, 3 and completes. `from()` accepts any `ObservableInput`: an Observable (returned as-is), an object implementing `Symbol.observable`, an array or array-like (so a string emits its characters), a Promise or other thenable, an async iterable, an iterable such as a `Map` or generator, or a `ReadableStream`. Anything else throws a `TypeError` when `from()` is called. `of` and `from` over arrays or iterables emit synchronously during `subscribe()`; `from(promise)` emits later, when the promise resolves.

code

ts · 17 lines
ts
import { from, of } from 'rxjs';

of([1, 2, 3]).subscribe((v) => console.log('of:', v));
// of: [1, 2, 3]

from([1, 2, 3]).subscribe((v) => console.log('from:', v));
// from: 1
// from: 2
// from: 3

from(new Map([['a', 1]])).subscribe((v) => console.log('map entry:', v));
// map entry: ['a', 1]

from(Promise.resolve(42)).subscribe((v) => console.log('promise:', v));
console.log('after subscribe');
// after subscribe
// promise: 42

go deeper

for a junior

Recall that of emits its arguments and from unpacks one input, and give the array example both ways.

for a middle

List the ObservableInput kinds from() accepts, and say which ones emit synchronously and which emit when a promise resolves.

for a senior

Watch for code that relies on from(promise) being lazy, and for per-item logic silently receiving whole arrays because of() was used.

for a principal

Encourage APIs that accept ObservableInput rather than Observable only, so callers can pass arrays or promises without wrapping them.

## Two creation functions, two jobs RxJS's **creation functions** build an `Observable` from something that already exists. `of` and `from` are the two most used, and they are easy to confuse because both can take an array. - **`of(...values)`** treats each argument as one value. It emits the arguments in order, then completes. - **`from(input)`** takes a single input and converts it, **unpacking** whatever it contains. | Expression | Emits | Then | |---|---|---| | `of(1, 2, 3)` | `1`, `2`, `3` | complete | | `of([1, 2, 3])` | `[1, 2, 3]` (one value) | complete | | `from([1, 2, 3])` | `1`, `2`, `3` | complete | | `of()` | nothing | complete | | `from('abc')` | `'a'`, `'b'`, `'c'` | complete | Under the hood, `of` simply calls `from` on its argument list, which is why `of(1, 2, 3)` and `from([1, 2, 3])` behave the same. ## What from() accepts `from()` accepts the type RxJS calls **`ObservableInput`**. RxJS 7 checks the input in this order: 1. an RxJS `Observable` - returned unchanged; 2. an object implementing **`Symbol.observable`** (an interop observable from another library); 3. an **array-like** value - arrays, `arguments`, typed arrays and strings; 4. a **Promise** or any thenable - it emits the resolved value then completes, or errors on rejection; 5. an **async iterable** - such as an async generator; 6. an **iterable** - a `Set`, a `Map` (yielding `[key, value]` pairs) or a generator; 7. a **ReadableStream**-like object. If none of these match - a plain object, a number, `null` - `from()` throws a `TypeError` whose message reads "You provided ... where a stream was expected". The check happens when `from()` is **called**, not when the result is subscribed, so the error surfaces at the call site. The same `ObservableInput` type is what operators accept wherever they need an inner source, which is why a function that returns a Promise or an array can be used almost anywhere RxJS expects an Observable. ## Timing and completion Both functions **complete** once the input is exhausted, but they differ in when values arrive: - `of(...)`, `from(array)` and `from(iterable)` emit **synchronously**: every value and the completion are delivered before `subscribe()` returns. - `from(promise)` emits **asynchronously**, when the promise's reaction runs - even for a promise that has already resolved. - `from(asyncIterable)` and `from(readableStream)` emit asynchronously as items become available. The array loop also checks whether the subscriber has closed before each value, so an operator such as `take(2)` downstream stops a large array early instead of walking all of it. ## Common mistakes - Writing `of(items)` when the code needs one emission per item. The subscriber receives the whole array once, and per-item operators never see individual items. - Writing `from(items)` when the code needs the array as a single value - for example, when feeding a list into a stream of "current list" states. - Passing a plain object to `from()` and expecting its properties to be emitted. Objects are not iterable; use `from(Object.entries(obj))` instead. - Believing `from(promise)` makes the promise lazy. The promise has already started; `from` only adapts its result. Deferring the promise's creation is `defer`'s job. ## from() as a normaliser Because `from()` returns an RxJS `Observable` unchanged, it is cheap to call on something that might already be one. That makes it a handy normaliser at API boundaries: - a helper that accepts `ObservableInput<T>` can call `from(input)` once and then use operators, whatever the caller passed; - an Observable from another library that implements `Symbol.observable` is adapted into an RxJS one, so RxJS operators can be applied to it; - a callback that sometimes returns an array and sometimes a promise can be handled by the same code path. ## Deprecated forms to recognise Older code passes a scheduler as the last argument - `of(1, 2, 3, asyncScheduler)` or `from(array, scheduler)`. That argument is deprecated since RxJS 6.5 and scheduled for removal in v8; the replacement is `scheduled(input, scheduler)`. Explicit type arguments on `of<T>()` with no values are also deprecated. ## In an interview The one-line answer is "`of` emits its arguments, `from` unpacks its input". A strong candidate adds the list of `ObservableInput` types, notes that `from(promise)` is asynchronous while `of` and `from(array)` are synchronous, and knows that `from()` on an unsupported value throws immediately rather than erroring through the stream.

  • In RxJS, what happens if you call from() with a plain object such as { a: 1 }?
    It throws a `TypeError` immediately, at the `from()` call, because a plain object is not an `ObservableInput`: it is not array-like, not a thenable and not iterable. The message says an Observable, Promise, ReadableStream, Array, AsyncIterable or Iterable was expected. To stream its properties, pass `Object.entries(obj)` to `from()` instead.
  • In RxJS 7, how do you make of(1, 2, 3) emit asynchronously now that the scheduler argument is deprecated?
    Use `scheduled([1, 2, 3], asyncScheduler)`, which RxJS added as the replacement for the scheduler argument on `of` and `from`. Each value is then delivered through the scheduler instead of inside the `subscribe()` call. How the individual schedulers differ is a separate topic.

saying these in an interview costs you the question

  • of([1, 2, 3]) emits 1, 2 and 3 as separate values.
  • from() over a plain object emits each property.
  • from(promise) makes the promise lazy, so it starts on subscribe.
  • of and from always emit asynchronously.
  • from() on an unsupported value errors through the stream when subscribed.