JavaScript on the Desktop

Fast and Slow


CGI


Battlefield 1


Nvidia GeForce Experience


Adobe
Creative Suite


All the Electron Apps

Let's Talk Performance

🚀

💾

Memory

Speed

🔋

Energy

const isReachable = require('is-reachable')

async function isDotReachable() {
  const dotReachable = await isReachable('dotjs.io')

  console.log(dotReachable)
}

1️⃣ Modules

94000

Lines of JSON

const isReachable = require('is-reachable')

async function isDotReachable() {
  const dotReachable = await isReachable('dotjs.io')

  console.log(dotReachable)
}

Modules

// Don't require the module until you need it
// require() caches results

async function isDotReachable() {
  const isReachable = require('is-reachable')
  const dotReachable = await isReachable('dotjs.io')

  console.log(dotReachable)
}
// Do you even need a module?

function isDotReachable() {
  return new Promise((resolve, reject) => {
    const http = require('http')

    http.get('http://dotjs.io', () => {
      resolve()
    }).on('error', (e) => {
      reject(e)
    })
  })
}
#!/usr/bin/env node

'use strict';

[...]

try {
  require(__dirname + '/../lib/v8-compile-cache.js');
} catch (err) {

}

[...]

V8 Code Caching

require('v8-compile-cache')

async function isDotReachable() {
  // The "compiled" blobs are now cached
  // Have V8 parse the module only once
  const isReachable = require('is-reachable')
  const dotReachable = await isReachable('dotjs.io')

  console.log(dotReachable)
}

Careful, Cowboy

might cost you

npm install
Turning your code
into pixels

Make it easy

2️⃣

// Option 1

const divs = document.querySelectorAll('.test-class')

// Option 2

const divs = document.getElementsByClassName('test-class')

// 🤔 Which one is faster? And by how much? Let's find out:
// https://jsperf.com/dotjs-perf-example

3️⃣ Not all code is equal

document.querySelectorAll('.test-class')

 

375,363 ops/sec

 

 

±5.27%, 99% slower

document.getElementsByClassName('test-class')

 

29,318,183 ops/sec

 

±0.55%

#include <nan.h>

NAN_METHOD(IsPrime) {
    if (!info[0]->IsNumber()) {
        Nan::ThrowTypeError("Argument must be a number!");
        return;
    }
    
    int number = (int) info[0]->NumberValue();
    
    if (number < 2) {
        info.GetReturnValue().Set(Nan::False());
        return;
    }
    
    for (int i = 2; i < number; i++) {
        if (number % i == 0) {
            info.GetReturnValue().Set(Nan::False());
            return;
        }
    }
    
    info.GetReturnValue().Set(Nan::True());
}

NAN_MODULE_INIT(Initialize) {
    NAN_EXPORT(target, IsPrime);
}

NODE_MODULE(addon, Initialize);

Go Native

// Brought to you by Parcel:
// Import a wasm file like it's no big deal
const { add } = await import('./add.wasm')
const result = add(2, 3)

// 🙀 Same trick, but with Rust
const { add } = await import('./add.rs')
const result = add(2, 3)
// Rust, a true teamplayer

#[no_mangle]
pub fn add(a: i32, b: i32) -> i32 {
  return a + b
}
Application
Lifecycle

Respect it

4️⃣

document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    // Suspend all expensive operations
    // 🔪 setInterval()
    // 🔪 Animations
    // 🔪 Not-urgent network requests
  } else {
    // Continue!
  }
})

Background

@felixrieseberg

JavaScript on the Desktop, Fast and Slow

By Felix Rieseberg