Typolino – Web Worker

Just for fun I was thinking to add web workers to fetch the image download URLs and also the images in a web worker. If the URL is already there, perfect, if not wait for the web worker to complete its task.

Actually web workers are quite well supported and it is super simple to add your own web worker to an Angular application. I think the easiest way to describe a web worker would be a background script or some sort of thread that you can spawn that does some things.

If you have a strong Java background you are for sure familar with threads and the related issues if you misuse synchronization locks. Web workers are simpler as they provide a clear interface to communicate with your application. You don’t have to care about synchronization.

Create a Web Worker

To create a new web worker we can use our beloved ng g command.

ng g web-worker image-loader

This will create a more or less empty web worker that we can use. The web worker interface is really simple:

  • We can post messages to it
  • We can get messages from it

So what we would like to achieve: once we are starting a Typolino lesson we pass it the lesson to load the data in the background. Once we’ve got an image URL we try to fetch it. To be honest I’m not 100% sure if we win anything (maybe it’s even worse given the serialization overhead) as the operations are anyhow asynchronous by nature – but why not try it with web workers.

/// <reference lib="webworker" />

import { Lesson } from './lesson';
import { environment } from '@typolino/environments/environment';
import * as firebase from 'firebase';

const firebaseConfig = environment.firebase;
firebase.initializeApp(firebaseConfig);

addEventListener('message', ({ data }) => {
  const lesson = data as Lesson;
  lesson.words.forEach((word, index) => {
    firebase
      .storage()
      .ref(`${lesson.id}/${word.imageId}`)
      .getDownloadURL()
      .then((url) =>
        Promise.all([
          fetch(url, {
            mode: 'no-cors',
            cache: 'default',
          }),
          Promise.resolve(url),
        ])
      )
      .then(([response, url]) => {
        postMessage({
          index,
          url,
        });
      });
  });
});

Create the worker..

const worker = new Worker('@typolino/app/image-loader.worker', {
  type: 'module',
});

..and in the lesson.component

 ngOnInit(): void {
    this.route.paramMap
      .pipe(
        first(),
        map((params) => params.get('lessonId')),
        switchMap((lessonId: string) =>
          this.lessonService.selectLesson(lessonId)
        )
      )
      .subscribe((lesson) => {
        this.lesson = lesson;
        this.setupWord(this.lesson.words[0]); 

        worker.onmessage = ({ data }) => {
          this.lesson.words[data.index].imageUrl = data.url;
        };

        worker.postMessage(this.lesson);
      });
  }

It works! When we load a lesson we see that the browser is fetching all the URLs and images. As soon as the first image is available it should be rendered and the data is read from the cache as expected.

Conclusion

Using web workers is quite straight forward. Of course it’s a bit cheap as I’m only supporting modern browsers – but it is way more fun to code like this. When using Typolino the images are just there – for me it feels really fast when using the application. There are definitely other techniques but it was fun trying it out.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.