SANS

GERARD

Developer Evangelist

Developer Evangelist

International Speaker

Spoken at 164 events in 37 countries

Serverless Training

Serverless Training

Browser Performance

Icons made by Gregor Cresnar from www.flaticon.com is licensed by CC 3.0 BY
Icons made by Darius Dan from www.flaticon.com is licensed by CC 3.0 BY

DOM events

background Layer 1

HTTP Requests

Asynchronous Web APIs

setTimeout setInterval

Promises async/await

Woot!? Event Loop?

Macro & microtasks Quiz

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

Event Loop waits for microtasks

JavaScript:  call me maybe?

setTimeout Quiz

setTimeout(() => log(1));
setTimeout(() => log(2), 0);
log(3)

// a) 1 2 3
// b) 3 2 1
// c) 3 1 2

YAY!

setTimeout Quiz

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)

setInterval(task, 50)

Time

50ms

task 1

task 2

task 3

task < delay

setInterval(task, 50)

Time

50ms

task 1

task 2

task 3

task > delay

task 1

task 2

task 3

Reality

Ideal

Delay is only timer to enter Event Loop

RxJS help me!

audit auditTime buffer bufferCount bufferTime bufferToggle bufferWhen catchError combineLatestAll combineLatestWith concatAll concatMap concatMapTo concatWith connect count debounce debounceTime defaultIfEmpty delay delayWhen dematerialize distinct distinctUntilChanged distinctUntilKeyChanged elementAt endWith every exhaustAll exhaustMap expand filter finalize find findIndex first groupBy ignoreElements isEmpty last map mapTo materialize max merge mergeAll mergeMap mergeMapTo mergeScan mergeWith min observeOn onErrorResumeNext pairwise raceWith reduce repeat repeatWhen retry retryWhen sample sampleTime scan sequenceEqual share shareReplay single skip skipLast skipUntil skipWhile startWith subscribeOn switchAll switchMap switchMapTo switchScan take takeLast takeUntil takeWhile tap throttle throttleTime throwIfEmpty timeInterval timeout timeoutWith timestamp toArray window windowCount windowTime windowToggle windowWhen withLatestFrom zipAll zipWith

98 operators

rxjs.dev

Basic Example

of(1, 2, 3)
.subscribe(
  v => console.log(v)
);

1

2

3

Synchronous Operators

Synchronous by default

  • Examples: of, from, range
  • Default Scheduler: queue

Asynchronous Operators

Asynchronous by default

  • Examples: timer, interval
  • Scheduler: async
  • Primitive: setInterval

Changing default Scheduler

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

Least Concurrency Principle

Queue Scheduler

Overview

  • Executes Synchronously
  • Tasks execute in order
  • Waits until current task ends before executing next one
  • Performant (precedes Event Loop)

QueueScheduler Example

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

Asap Scheduler

Overview

  • Executes Asynchronously (micro)
  • Tasks execute before next tick
  • Relays on Promises
  • Performant (precedes Event Loop)

AsapScheduler Example

import { asapScheduler } from 'rxjs';
import { queueScheduler } from 'rxjs';

setTimeout(() => log(1));
asapScheduler.schedule(() => log(2)); 
queueScheduler.schedule(() => log(3));

// 3 2 1

Async Scheduler

Overview

  • Executes Asynchronously (macro)
  • Relays on setInterval
  • Less performant (uses Event Loop)

AsyncScheduler Example

import { asyncScheduler } from 'rxjs';
import { queueScheduler } from 'rxjs';

asapScheduler.schedule(() => log(2)); 
asyncScheduler.schedule(() => log(1));
queueScheduler.schedule(() => log(3));

// 3 2 1

Animations Scheduler

Overview

  • Executes Asynchronously
  • Relays on requestAnimationFrame
  • Adapts to Device Frame Rate
  • Slows down when is not active
  • Balances CPU/GPU load

DURATION

Animations

0%

100%

KEYFRAMES

Frame Rates

60 FPS

Time

16.66ms

16.66ms

16.66ms

1000/60ms

paint

frame

paint

frame

paint

frame

Stutter or Losing Frames!

60 FPS

Time

16.66ms

16.66ms

16.66ms

paint

frame

paint

frame

paint

frame

using setInterval

const token = setInterval(paintFrame, 1000/60);

const paintFrame = () => {
  // animation code
}
 
setTimeout(() => clearInterval(token), 40);

Issues

  • Ignores Device Frame Rate
  • Runs all the time (batteries enemy)
  • Ignores current CPU/GPU load

requestAnimationFrame

60 FPS

Time

16.66ms

16.66ms

16.66ms

1000/60ms

paint

frame

paint

frame

paint

frame

requestAnimationFrame

let token = requestAnimationFrame(paintFrame);

const paintFrame = (timestamp) => {
  // animation code
  token = requestAnimationFrame(paintFrame)
}

setTimeout(() => cancelAnimationFrame(token), 40);

animationFrames (RxJS)

let state = { angle: 0 };
const div = document.querySelector('.circle');

animationFrames().pipe(
  tap(render)
).subscribe();

function render({ timestamp, elapsed }) {
  let { angle } = state;
  state = { angle: ++angle % 360 };
  (div as HTMLDivElement).style.transform = `rotate(${angle}deg)`;
}

Wrapping up

Schedulers priorities RxJS

# Type Execution Primitives
1 queueScheduler Sync scheduler.schedule(task, delay)
scheduler.flush()
2 asapScheduler Async (micro) Promise.resolve().then(() => task)
3 animationFrameScheduler Async (rAf) id = requestAnimationFrame(task)
cancelAnimationFrame(id)
4 asyncScheduler Async (macro) id = setInterval(task, delay)
clearInterval(id)

Next tick   -   Next tick   -   Next tick   -   Next tick   -   Next tick   -   Next tick

stackblitz.com/@gsans