by Gerard Sans | @gerardsans
Spoken at 69 events in 23 countries
900
1.4K
const log = console.log;
const macro = v => setTimeout(() => log(v));
const micro = v => Promise.resolve().then(() => log(v));
log(1);
macro(2);
micro(3);
log(4);
a) 1 2 3 4
b) 1 4 2 3
c) 1 4 3 2
setTimeout(() => log(1));
setTimeout(() => log(2), 0);
log(3)
// a) 1 2 3
// b) 3 2 1
// c) 3 1 2
YAY!
setTimeout(() => log(1), 1000);
setTimeout(() => log(2), 2000);
setTimeout(() => log(3), 3000);
// a) 1 2 3 (wait aprox. 1s)
// b) 1 2 3 (wait aprox. 3s)
// c) 1 2 3 (wait aprox. 6s)
Time
50ms
task 1
task 2
task 3
task < delay
Time
50ms
task 1
task 2
task 3
task > delay
task 1
task 2
task 3
Reality
Ideal
ajax, audit, auditTime, bindCallback, bindNodeCallback, buffer, bufferCount, bufferTime, bufferToggle, bufferWhen, cache, catch, combineAll, combineLatest, concat, concatAll, concatMap, concatMapTo, count, create, debounce, debounceTime, defaultIfEmpty, defer, delay, delayWhen, dematerialize, distinct, distinctKey, distinctUntilChanged, distinctUntilKeyChanged, do, elementAt, empty, every, exhaust, exhaustMap, expand, filter, finally, find, findIndex, first, forkJoin, from, fromEvent, fromEventPattern, fromPromise, generate, groupBy, ignoreElements, interval, isEmpty, last, let, map, mapTo, materialize, max, merge, mergeAll, mergeMap, mergeMapTo, mergeScan, min, multicast, never, observeOn, of, pairwise, partition, pluck, publish, publishBehavior, publishLast, publishReplay, race, range, reduce, repeat, repeatWhen, retry, retryWhen, sample, sampleTime, scan, share, single, skip, skipLast, skipUntil, skipWhile, startWith, subscribeOn, switch, switchMap, switchMapTo, take, takeLast, takeUntil, takeWhile, throttle, throttleTime, throw, timeInterval, timeout, timeoutWith, timer, timestamp, toArray, toPromise, Utility Operators, window, windowCount, windowTime, windowToggle, windowWhen, withLatestFrom, zip, zipAll
of(1).subscribe(v => l(v));
l(2);
// 1 2
import { async } from 'rxjs/scheduler/async';
of(1, async).subscribe(v => l(v));
l(2);
// 2 1
// before
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/filter';
Observable.interval(1000)
.filter(x => x%2!==0) // --1--3--5--
// after
import { Observable } from 'rxjs/Observable';
import { interval } from 'rxjs/observable/interval';
import { filter } from 'rxjs/operators';
interval(1000)
.pipe(
filter(x => x%2!==0)
)
Timeline
Emitted values
complete
next
next
next
// (111|)
let a$ = Rx.Observable.create(observer => {
observer.next(1);
observer.next(1);
observer.next(1);
observer.complete();
});
let subscription = a$.subscribe({
next: v => log(v),
complete: () => log('|')
});
// --1--1--1|
let a$ = Rx.Observable.create(observer => {
setTimeout(() => observer.next(1));
setTimeout(() => observer.next(1));
setTimeout(() => observer.next(1));
setTimeout(() => observer.complete());
});
let subscription = a$.subscribe({
next: v => log(v),
complete: () => log('|')
});
complete
next
next
next
.map(v => v)
.subscribeOn(async)
.subscribe()
complete
next
next
next
.map(v => v)
.observeOn(async)
.subscribe()
of(1).subscribeOn(async)
.subscribe({
next: x => log(x),
complete: () => log('3')
});
log('2');
// a) 1 2 3
// b) 2 1 3
// c) 1 3 2
YAS!
Type | Execution | Primitives |
---|---|---|
queue | Sync | scheduler.schedule(task, delay) scheduler.flush() |
asap | Async (micro) | Promise.resolve().then(() => task) |
async | Async (macro) | id = setInterval(task, delay) clearInterval(id) |
animationFrame | Async | id = requestAnimationFrame(task) cancelAnimationFrame(id) |
import { queue } from 'rxjs/scheduler/queue';
queue.schedule(() => log(1));
log(2);
queue.schedule(() => log(3));
// 1 2 3
queue.schedule(() => {
queue.schedule(() => log(1));
log(2);
queue.schedule(() => log(3));
});
// 2 1 3
import { asap } from 'rxjs/scheduler/asap';
import { queue } from 'rxjs/scheduler/queue';
setTimeout(() => log(1));
asap.schedule(() => log(2));
queue.schedule(() => log(3));
// 3 2 1
import { async } from 'rxjs/scheduler/async';
import { queue } from 'rxjs/scheduler/queue';
asap.schedule(() => log(2));
async.schedule(() => log(1));
queue.schedule(() => log(3));
// 3 2 1
import { AsyncScheduler } from 'rxjs/scheduler/AsyncScheduler';
import { AsyncAction } from 'rxjs/scheduler/AsyncAction';
const s = new AsyncScheduler(AsyncAction);
const DELAY = 0;
let subscription;
subscription = s.schedule(v => log(v), DELAY, 1);
s.schedule(v => log(v), DELAY, 2);
log(3);
subscription.unsubscribe();
// 3
// 2
import { AsyncScheduler } from 'rxjs/scheduler/AsyncScheduler';
import { AsyncAction } from 'rxjs/scheduler/AsyncAction';
const s = new AsyncScheduler(AsyncAction);
const DELAY = 2000;
const start = Date.now();
s.schedule(v => log(v), DELAY, 1);
s.schedule(v => log(v), DELAY, 2);
s.schedule(() => log(`${s.now()-start}ms`), DELAY);
log(3);
// 3
// 1
// 2
// 2008ms
FRAMES
60 FPS
Time
16.66ms
16.66ms
16.66ms
1000/60ms
paint
frame
paint
frame
paint
frame
paint
frame
paint
frame
60 FPS
Time
16.66ms
16.66ms
16.66ms
const token;
const paintFrame = () => {
// animation code
token = setInterval(paintFrame, 1000/60);
}
paintFrame();
setTimeout(() => clearInterval(token), 2000);
60 FPS
Time
16.66ms
16.66ms
16.66ms
1000/60ms
paint
frame
paint
frame
paint
frame
const token;
const paintFrame = (timestamp) => {
// animation code
token = requestAnimationFrame(paintFrame)
}
requestAnimationFrame(paintFrame);
setTimeout(() => cancelAnimationFrame(token), 2000);
import { animationFrame } from 'rxjs/scheduler/animationFrame';
const DELAY = 0;
const state = { angle: 0 }
const div = document.querySelector('.circle');
const work = state => {
let {angle} = state;
div.style.transform = `rotate(${angle}deg)`;
animationFrame.schedule(work, DELAY, { angle: ++angle%360 });
}
animationFrame.schedule(work, DELAY, state);
import { VirtualTimeScheduler } from 'rxjs/scheduler/VirtualTimeScheduler';
import { VirtualAction } from 'rxjs/scheduler/VirtualTimeScheduler';
const s = new VirtualTimeScheduler(VirtualAction);
const start = Date.now();
s.schedule(v => log(v), 2000, 2); // tasks are sorted by delay
s.schedule(v => log(v), 50, 1);
s.flush(); // manual execution (synchronous)
log(3);
log(`VirtualTimeScheduler: ${s.now()}ms`) // virtual time
log(`Execution: ${Date.now()-start}ms`) // instant execution
// 1
// 2
// 3
// VirtualTimeScheduler: 2000ms
// Execution: 6ms
import { VirtualTimeScheduler } from 'rxjs/scheduler/VirtualTimeScheduler';
import { VirtualAction } from 'rxjs/scheduler/VirtualTimeScheduler';
const s = new VirtualTimeScheduler(VirtualAction);
const start = Date.now();
interval(3600000, s).pipe(take(24)) // 1 hour (3600 x 1000)ms x 24 hours
.subscribe(v => log(v));
s.flush();
log(3);
log(`VirtualTimeScheduler: ${s.now()}ms`)
log(`Execution: ${Date.now()-start}ms`)
// 0...23
// 3
// VirtualTimeScheduler: 86400000ms (1 day)
// Execution: 25ms
import {marbles} from "rxjs-marbles";
describe("Cold Observables", () => {
describe("basic marbles", () => {
it("should support simple values as strings", marbles(m => {
const values = { a: 1 };
const input = m.cold("--a--a--a|", values);
const expected = m.cold("--1--1--1|");
const output = input.map(v => v);
m.expect(output).toBeObservable(expected);
}));
});
});