by Gerard Sans | @gerardsans
Spoken at 87 events in 25 countries
900
1.5K
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
audit auditTime buffer bufferCount bufferTime bufferToggle bufferWhen catchError combineAll combineLatest concat concatAll concatMap concatMapTo count debounce debounceTime defaultIfEmpty delay delayWhen dematerialize distinct distinctUntilChanged distinctUntilKeyChanged elementAt endWith every exhaust exhaustMap expand filter finalize find findIndex first groupBy ignoreElements isEmpty last map mapTo materialize max merge mergeAll mergeMap mergeMap as flatMap mergeMapTo mergeScan min multicast observeOn onErrorResumeNext pairwise partition pluck publish publishBehavior publishLast publishReplay race reduce repeat repeatWhen retry retryWhen refCount sample sampleTime scan sequenceEqual share shareReplay single skip skipLast skipUntil skipWhile startWith subscribeOn switchAll switchMap switchMapTo take takeLast takeUntil takeWhile tap throttle throttleTime throwIfEmpty timeInterval timeout timeoutWith timestamp toArray window windowCount windowTime windowToggle windowWhen withLatestFrom zip zipAll
of(1).subscribe(v => l(v));
l(2);
// 1 2
import { asyncScheduler } from 'rxjs';
of(1, asyncScheduler).subscribe(v => l(v));
l(2);
// 2 1
do throw switch finally
tap throwError switchAll finalize
import { interval } from 'rxjs';
import { filter } from 'rxjs/operators';
const isOddNumber = (x: number) => x%2!==0;
interval(1000).pipe(
filter(isOddNumber)
);
// --0--1--2--3--4--5-- interval
// -----1-----3-----5-- filter
import { throwError } from 'rxjs';
import { catchError, finalize } from 'rxjs/operators';
throwError("💩").subscribe({
error: e => l(`Error: ${e}`)
})
throwError("💩").pipe(
catchError(e => of("No worries. Sorted!😃")),
finalize(() => { /* code */ }),
).subscribe(v => l(v));
Timeline
Emitted values
complete
next
next
next
// (111|)
let a$ = 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$ = 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(asyncScheduler)
.subscribe()
complete
next
next
next
map(v => v)
observeOn(asyncScheduler)
.subscribe()
of(1).pipe(subscribeOn(asyncScheduler))
.subscribe({
next: x => l(x),
complete: () => l('3')
});
l('2');
// a) 1 2 3
// b) 2 1 3
// c) 1 3 2
import { queueScheduler } from 'rxjs';
queueScheduler.schedule(() => log(1));
log(2);
queueScheduler.schedule(() => log(3));
// 1 2 3
queueScheduler.schedule(() => {
queueScheduler.schedule(() => log(1));
log(2);
queueScheduler.schedule(() => log(3));
});
// 2 1 3
import { asapScheduler } from 'rxjs';
import { queueScheduler } from 'rxjs';
setTimeout(() => log(1));
asapScheduler.schedule(() => log(2));
queueScheduler.schedule(() => log(3));
// 3 2 1
import { asyncScheduler } from 'rxjs';
import { queueScheduler } from 'rxjs';
asapScheduler.schedule(() => log(2));
asyncScheduler.schedule(() => log(1));
queueScheduler.schedule(() => log(3));
// 3 2 1
import { asyncScheduler } from 'rxjs';
const s = asyncScheduler;
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';
const s = asyncScheduler;
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 { animationFrameScheduler } from 'rxjs';
const s = animationFrameScheduler;
const DELAY = 0;
const state = { angle: 0 }
const div = document.querySelector('.circle');
const work = state => {
let {angle} = state;
div.style.transform = `rotate(${angle}deg)`;
s.schedule(work, DELAY, { angle: ++angle%360 });
}
s.schedule(work, DELAY, state);
Type | Execution | Primitives |
---|---|---|
queueScheduler | Sync | scheduler.schedule(task, delay) scheduler.flush() |
asapScheduler | Async (micro) | Promise.resolve().then(() => task) |
asyncScheduler | Async (macro) | id = setInterval(task, delay) clearInterval(id) |
animationFrameScheduler | Async | id = requestAnimationFrame(task) cancelAnimationFrame(id) |