Gerard Sans | Axiom 🇬🇧 PRO
Founder of Axiom Masterclass, professional trainings // Forging skills for the new era of AI. GDE in AI, Cloud & Angular. Building London's tech & art nexus @nextai_london. Speaker | MC | Trainer.
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 2setTimeout(() => log(1));
setTimeout(() => log(2), 0);
log(3)
// a) 1 2 3
// b) 3 2 1
// c) 3 1 2YAY!
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 2YAS!
| 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 3import { asap } from 'rxjs/scheduler/asap';
import { queue } from 'rxjs/scheduler/queue';
setTimeout(() => log(1));
asap.schedule(() => log(2));
queue.schedule(() => log(3));
// 3 2 1import { 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 1import { 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
// 2import { 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
// 2008msFRAMES
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: 6msimport { 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: 25msimport {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);
}));
});
});By Gerard Sans | Axiom 🇬🇧
Observables have been very popular because of their many qualities: asynchronous processing, composition, performance, powerful operators. But usually there's a less covered feature that lies beneath. That is: Schedulers. In this talk we are going to cover Schedulers in depth, going from the basic APIs to more obscure features to bend time to our will!
Founder of Axiom Masterclass, professional trainings // Forging skills for the new era of AI. GDE in AI, Cloud & Angular. Building London's tech & art nexus @nextai_london. Speaker | MC | Trainer.