

Define a web 4.0 app to be multi threaded
source link: https://itnext.io/define-a-web-4-0-app-to-be-multi-threaded-9c495c0d0ef9
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Define a web 4.0 app to be multi threaded
Take your skills to an entirely new level by learning to create a visually stunning and blazing fast next generation web app using web workers.
The app as well as your components will live within an application web worker.
Content
- What are we going to build?
- Prerequisites
- Setting up the Infrastructure
- Using npx neo-app to create the app shell
- The three different environments
- Inspecting the output
- Class configs & basic concepts
- Creating the HeaderContainer
- Creating the MainContainerController
- Connecting our app to the disease.sh API
- Deploying your app for production
- Summary
- Final thoughts
1. What are we going to build?
We will start from scratch going step by step and progressively enhance our app, while looking into basic architecture and framework concepts as well.
Don’t be afraid :)
While the final result is definitely complex, this tutorial is as easy as possible. High level components like the 3d gallery and helix already exist, so we don’t need to create them on our own.
To keep the scope reasonable, I will split the tutorial into multiple parts.
Welcome to part 1!
We will start with a single page app (SPA) version and transform it into a multi window app inside the last part of this blog series.
The app is already finished, so you can take a look at the full source code. The final result obviously goes beyond Part 1 of the tutorial, so think twice if you want to look into this code early.
apps/covid (single page app version)
apps/sharedcovid (multi window version)
2. Prerequisites
To follow this tutorial, you need:
- a solid understanding of Javascript, CSS & HTML
- to be familiar with the ES6+ based class system
- to know the concepts of OOP
- Google Chrome
You do not need any prior experience in using frameworks / libs like Angular, React or neo.mjs.
It would literally be impossible to create this app using Angular or React (at least the multi window version), so neo.mjs is the obvious choice.
3. Setting up the Infrastructure
There are 2 different options to create a neo.mjs app.
Important: We will stick to option two.
Option one:
- Clone the neo.mjs repo
- Run npm install
- Run the build-all program inside the package.json
- Run the create-app program and your new app folder appears under “apps”:

Especially in case you want to work on the framework source code as well, like adding new components or a new theme, it makes sense to start this way. You can later on move your app folder into a real app shell.
Option two:
We can create a new app shell (workspace) with simply entering npx neo-app
into our terminal. This way we can keep your own code base separate from the framework and just use neo.mjs as a node module.
[Optional] Before we do this, let us create a new GitHub repository first:
And there we go:
You can see the result of this tutorial (part 1) inside the repo:
github.com/neomjs/tutorial-shared-covid-app-part-1
Feel free to create an own repo as well, but this is optional for this tutorial.
I will clone the new repo next:
As you might already see I am using WebStorm. Feel free to stick to any IDE you feel comfortable with.
4. Using npx neo-app to create the app shell
We are getting close to finish everything we need to start coding. The last missing step is to create the neo.mjs app shell.
To do this, we need to open our terminal (or CMD on Windows) and enter the folder 1 level above our fresh repo folder:

Let us take a look at our program options. Enter npx neo-app --help
:

[Side note] I am using @latest
to ensure getting the newest version.
There are some options which you can pass on the command line if you want to, but there is also a visual interface in place in case you don’t.
We can change all passed options later on.
Now enter npx neo-app
:

In case you are below version 2.3, please add the latest flag.
The program will ask you for your workspace (app shell folder) name. The important part here is to match the name of the repo folder.

The next question is the app name. Since this also equals the namespace inside our code base, it should be PascalCase. Let’s keep it short: Covid.

We do want to use both themes and dynamically switch between them at run time, so just hit enter.

The main thread addons DragDrop
and Stylesheet
are already selected. We will need more later on. For now, just hit enter again.

The last question is if we want to use dedicated workers or SharedWorkers.
While our final version will use shared workers, I strongly recommend to start with “normal” ones. Just hit enter again.
[Side note] The reason to not use shared workers directly is that this makes the debugging a bit more complicated. For workers you can see all console logs directly inside your browser devtools. For shared workers you need to open a new window for each of them:
chrome://inspect/#workers
The framework uses an abstraction layer on top of the workers communication, so we can switch later on with just changing one top level config. The API will stay exactly the same. Pretty damn cool, right? [End side note]
It takes a bit of time now, since a lot is happening on the background:
Total time for covid buildAll: 44.68s
Once done, the dev server will automatically open a new browser tab inside your default browser. In case this is not Google Chrome, you should copy the URL and enter it there.

Click on the “apps” folder, then click on “covid”:

Congratulations! You got your first neo.mjs app up and running.
This is pretty much your “Hello World” starting point.
Change the URL to “docs” next:
As you can see, you got the full framework source code documentation here, but also a documentation for your own new app. “Covid” is the name(space) we just picked.
[Side note] The docs app is build with neo.mjs as well. It is good for getting an overview of the class hierarchy as well as seeing all available class configs and methods. You can click on each one to enter a source code view. The docs app is not yet helpful though to learn working with the framework. For now it is limited to desktop browsers. In case you would like to see a mobile version of this app, feel free to open a ticket. Help appreciated. [End side note]
5. The three different environments
One design goal of the neo.mjs project is getting UI development back to where it belongs: directly into the browser.
development mode
The dev mode runs without any JavaScript builds or transpilations at all. We don’t need JS based source maps and can debug the real code directly without any external side effects or hot module replacements. This has saved me a lot of time already and is super convenient.
As you can see, we get the real code and on top of this, it is running inside our application worker.
The dev mode requires browsers to support JS modules as well as dynamic imports inside the worker scope. Chromium is ready for quite a while, so it works in Google Chrome and Microsoft Edge. Webkit (Safari) has resolved the ticket, but it is not yet live. The dev mode also works inside Safari Technology Preview.
Mozilla (Firefox) is not ready yet, but the team is actively working on it:
dist/development
In case you are using Angular or React, this is what you would call the dev mode. We are using webpack in a smart way to generate the split chunks we need including source maps for JS and CSS.
This mode runs in all major browsers.
dist/production
We are using webpack in a smart way to generate the split chunks we need without source maps for JS and CSS. The code is minified.
This mode runs in all major browsers.
6. Inspecting the output
Let us take a quick look at the output of the npx neo-app program:

We already have a .gitignore file in place which ignores the dist
folder as well as the node_modules
, so i am just callinggit add
on the top level folder and push it to the repo.
You can add more external dependencies here if you like to or create an html skeleton. Our main thread starting point includes the MicroLoader:
The MicroLoader will import the Main.mjs file of the framework. This one will create the workers setup. Your app will not live within the main thread.
The neo-config.json
file contains the framework options. The build programs will adjust this file for the different environments for us.
You will find all available framework configs inside DefaultConfig.mjs. E.g. themes defaults to
themes: ['neo-theme-light', 'neo-theme-dark']
which is the reason why it was not included inside our config file (we picked the default value → both).
Let us add the themes
config inside the config file and switch the order:
Reload your app inside the browser:

→ The order when using multiple themes matters, the first one will get applied by default.
Your main entry point is the app.mjs
file.
Think about this one as the “index.html” for our application web worker.
Once the neo.mjs main thread is done with creating the worker setup, the app worker will import the file and trigger onStart()
.
We can include a mainView
in case we want to put this one into the DOM right away, but this is optional. Again:
This is the main design goal of the neo.mjs framework: most parts of it as well as your apps run within the app worker and not inside the main thread.
The virtual dom engine can live inside its own thread as well and there is a thread for the data worker. Not important at this point.
Now is a good time for a quick break, since this is a lot to think about. Especially in case it is your first time using neo.mjs.
7. Class configs & basic concepts
Let us take a look at the main view of your new app:
We import the JavaScript modules (classes) which we are going to use at the top. This means, that the dev & dist versions of our app will only contain the files we use and not everything else to keep the total file size small. Make sure to add file name extensions to your imports, since your code is supposed to directly run inside a Browser.
[Side note] Meaning no bare module specifiers for the dev mode itself
It is important to think carefully about which base class you want to extend. Is your new class a utility file with no DOM related content? Go for e.g. core.Base
. In case there is DOM related content, go for component.Base
or classes extending this one.
E.g. the class hierarchy for Viewport is:
→ You can easily check the class hierarchy inside the Docs App (top right). The Docs App UI itself is written using neo.mjs, so you can dive into the source code to see another example on how to create Apps:
docs/app/view
Using a container.Viewport
as the base class will use the full available size of the screen (height & width 100%) and since it is extending container.Base
, you can add items.
In general neo.mjs is highly config driven using a custom class config system.
In short: You need to add configs (similar to class fields) inside the static getConfig() method.
static getConfig() { return {
// add your configs here
}}
The order of the configs does not matter. Configs are applied via
Object.defineProperty()
so they are get & set driven.
myButton.text = 'Something else';
Meaning you can dynamically change them just with assigning a new value and your UI will update (consistency: adding & changing configs the same way).
I just used “myButton”, meaning an instance of button.Base
. Time to look into the class here: src/button/Base.mjs#L81
I highlighted the line of the text config. If you look close, there is a trailing underscore:
text_: '',
A trailing underscore will get consumed by Neo.applyClassConfig(), which you can find at the end (right before the export) of all neo.mjs class definitions.
As a result of using the trailing underscore, the following methods will optionally become available:
beforeGetText(value)beforeSetText(value, oldValue)afterSetText(value, oldValue)
With this, he have the pre- and post-processing covered. Especially the “afterSetX” methods are very helpful for mapping configs to the virtual dom or firing events.

[Side note vdom] The part which matters is:
textNode.innerHTML = value;
→ We map the new value to the vdom. At the end of this method we are using:
me.vdom = vdom; // setter
In short: this “assignment” is sending the current vdom and the previous version to the vdom worker via main, the vdom worker will create the deltas, send those back to the main thread, main will apply them to the real dom and send a success response back to your scope: the App worker.
The vdom has an optional property called “removeDom” => you can remove a node from the real dom without removing it from the vdom this way. The benefit is to keep the structure of the vdom consistent. It can be convenient to alway have the iconCls as the first node and the text as the second node in case multiple methods can change them. [End side note vdom]
Important: You only use a trailing underscore once for each class config inside the class hierarchy. Example:
class MyButton extends Button {
static getConfig() { return {
text: 'My Button'
}}
}
No trailing underscore, since we already have it inside the Button class. The framework will warn you if you do add it again by mistake. We are just changing the value of the config, this will not override the business logic.
Working with instances:
const myButton = Neo.create(Button, {
iconCls: 'fa fa-home',
text : 'My Button'
});myButton.set({
iconCls: 'fa fa-user',
text : 'Something else'
});
- Please use
Neo.create()
instead of the new operator, since this will triggeronConstructed()
as well asinit()
internally. - Like with extending classes, you do not use a trailing underscore here (you are just passing values).
- You can use
set()
to change multiple configs at once. Especially when multiple configs get mapped to the vdom, this makes sense.
Bad example:
myButton.iconCls = 'fa fa-user';
myButton.text = 'Something else';
would change the configs the same way, but this would trigger the vdom engine twice. Using set()
, this only happens once. Since we do care about performance, set()
is the way to go.
8. Creating the HeaderContainer
The coding part can finally start :)
Let us go back to our MainContainer.mjs
file and remove the content of the dummy app:
Now reload your browser tab:

Especially in case you already have been using JavaScript before someone came up with the idea to move the entire UI development into nodejs, you might feel relieved now.
You just changed an ES8+ module, you reloaded your browser and your change is there. No build process or transpilation involved.
The reason is so trivial, but at this point worth mentioning: It works because … guess what … browsers are supposed to handle JavaScript.
The first thing to pick when using a Container is the layout. The most useful one is most likely Flexbox, with the extensions HBox and VBox (horizontal box, vertical box). Let us apply VBox and add some dummy items:

Nice, we got the country flag of Germany. You can use camelCase for your style definitions, or apply them as strings. I prefer the camelCase version.
Let us switch our layout to hbox and switch the colors for item 2 & 3:

You switched to the country flag of Belgium.
layout: {ntype: 'hbox', align: 'stretch', direction: 'row-reverse'},

You might have invented a new country flag. Easy, right?
For more infos on layout, the docs App is your best friend:
I am using the top left search field and unchecked the parent class checkboxes (top right).
Obviously can can dive into the code as well to look for config options:
src/layout/Flexbox.mjs
Difference to other common frameworks / libraries: a layout is extending core.Base
, not component.Base
since it does not have a DOM related output.
The question which you might think of is: “What is ntype?”
ntype
is just a convenience shortcut for Components which you know are already there. You remember for sure: Viewport is extending component.Base
at some point. So, Component has to be available inside our module.
You can of course import it:
and then switch ntype to module and use it directly. You will agree though that this feels like creating boiler plate code here, so we will remove it again.

Since we care about OOP, let us move the Header into its own class. We create the file HeaderContainer.mjs
inside our view folder:
A pretty basic class definition: We are extending container.Base
, we are using the HBox layout. The “cls” config will get applied to the vdom root node of this class, height is a shortcut to add a height style to the vdom root node.
We also added a logo component. This one is using an image tag instead of the default div tag, so we do add tag & src to the logo vdom object.
The next step is to include our new HeaderContainer into the MainContainer:
The cool thing here is that you actually can just drop a JS Module into the container items directly.
Reload your app browser tab:

Perfect! You just got an idea how to keep your code base modular & clean.
Since height is more like a layout driven config, let us remove it from the HeaderContainer class and add it to the instance config instead:
We switched to passing the HeaderContainer using the module
config, since this allows us to pass configs just for this instance and not affect the HeaderContainer class which we could use in other spots with a different height.
Reload your browser tab, the result is the same.
We will now add the other DOM markup into the HeaderContainer. This is really just HTML/ CSS. Just replace your HeaderContainer file with the following code:
Afterwards reload your browser tab again:

Now this already looks a lot closer to the app we are going to build.
I cheated a little bit here and already included the needed style definitions into the neo.mjs themes. Take a look at:
resources/scss/theme-dark/apps/covid/HeaderContainer.scss
resources/scss/src/apps/covid/HeaderContainer.scss
In case you look close, you will notice that exporting CSS variables is optional.
Looking at our current app, you will notice that
- clicking on the 2 buttons will throw errors
- there is no data yet
In case you would like to learn more about working with JSON based virtual DOM:
More input on application based themed styling:
9. Creating the MainContainerController
Let us start with the switch theme button inside our HeaderContainer.

I added a string based (click) handler, which does not exist yet. This explains the error when clicking the button. We could just map the handler to a method inside this class. The following screenshot is just an example, don’t write it. Something like:
- you can set configs inside methods like
constructor
oronConstructed
(which gets triggered after all constructors inside the class hierarchy are done). - you can use non string based handlers and directly map them to class methods.
We do like patterns like MVVM though. So a view controller feels needed here. We are in luck, controller.Component
is available.
Now we could just add a controller for the HeaderContainer itself, but since this one doesn’t really do much, let us just add a controller to the main container instead.
Add the file view/MainContainerController.mjs:
Just a basic setup. A quick look into the docs to check the class hierarchy:

It is not extending component.Base
.
Back to our MainContainer file, add the import statement, add the JS module into the controller config:
Lovely :)
The framework will create a controller instance for you when just dropping in the imported module.
Reload your browser tab:
We can see the onConstructed()
log which we added and now we also get 2 real errors, since the controller complains about the missing button handler mappings.
We learned something really important here:
- Not every view needs its own controller. In case a view does not have a controller on its own, it can communicate to the closest parent controller instead.
- Imagine we would have added a HeaderContainerController as well. Then you could either add the handler methods inside this controller or still stick to the MainContainerController and put them there. Or put some in 1 controller and the rest into the other. As it makes sense for your architecture. Get creative!
Add the 2 missing handlers, reload the browser tab, click on the buttons:
Alright, we can start working on the business logic.
Before we do this, I would like to show you something else regarding the “config driven” approach:
Expand the instance inside the console, scroll down and look for:
iconPosition: (...)
Click on the 3 dots (it is a getter) and they will change to
iconPosition: "left"
Okay, we just have to do this now:
Under the hood we just changed configs inside the app worker. These configs are tied to the button vdom, so the app worker will send the new & old vdom to the vdom worker, this one creates the deltas, sends them back to main, they get applied to the real dom and we get a success message inside our app scope.
Still not easy, but repeating this one feels important.
Back to our “Hello World”… I mean “Switch Theme” Button.
Replace onSwitchThemeButtonClick(data) with the following code:
Reload your browser tab and click the switch theme button:
When we now click our “Switch Theme” Button, 3 things happen
- We switch the theme, more precisely: since we are using CSS variables, we just switch the class on the MainContainer top level DOM node to switch to different CSS vars for each theme. You can apply themes to parts of your app as well (see e.g. the docs app as a live example).
- We exchanged the logo.
- We changed the
iconCls
of our “Switch Theme” Button
One important thing here is that we gave the logo a reference config:

This allows us to get the logo component instance inside the controller:

10. Connecting our app to the disease.sh API
An app without data is kind of boring, so as the second last Step of this tutorial, let us connect it to the following external API:
https://github.com/disease-sh/API
Let us enhance our MainContainerController a little bit:
We added:
- the apiSummaryUrl as a JS module variable
- loadSummaryData() just using fetch() on the endpoint
- applySummaryData() just logging the output
Reload your browser tab, hit the “Reload Data” Button:
Now this looks promising (not the content, that one is scary!).
Since we do want to format our numbers in a nice way and use the formatting methods in different spots, we add a last file:
covid/Util.mjs
We are extending core.Base
and are using a static method called formatNumber()
. We do not need to create an instance of this class.
Importing it into the MainContainerController and adding the applySummaryData()
logic next:
We are grabbing the total-stats container via reference as well as the last update text, then change the virtual dom via adding the API data formatted with our Utility class.
We also added onConstructed() to trigger a first call to the API right away (without the need to click on the Button).
At the end, there is once more the
container.vdom = vdom;
magic (you know, the worker ping pong game).
Reload your browser tab:

This is it, the final result for part 1!
11. Deploying your app for production
Now you will most likely want to see your app in Firefox & Safari as well.
Fair point :)
npm run build-all
Open the dist/development
or dist/production
versions in Firefox & Safari:
Firefox:
Safari (non tech preview):

Open your docs app again:
Since build-all contains the generate-docs task, you have the latest version of your app inside the docs now.
12. Summary
Wow! In case you are reading this I am extremely proud of you!
This was the hell of a tutorial with a massive amount of content.
You just learned:
- how to use npx neo-app to create an app shell
- the basic concepts of the config system
- how to modify your main view
- how to keep your architecture modular with separating classes
- how to create and use Component Controllers
- how to switch themes
- how to connect to an external API
In case you have questions, make sure to ask them!
At this point you are ready to create an app on your own.
After playing with neo.mjs a bit more, you could also contribute to the project. In case you like the concepts, enhance them. In case you don’t, start discussions what should change.
Join the Slack Channel:
13. Final thoughts
In case you would like to get more input on how to create your own components, there are some in depth guides inside the neo.mjs blog:
https://neomjs.github.io/pages/
For example:
You can find the neo.mjs framework repository here:
I already wrote a part 2 article for neo.mjs v1 a while ago. The logic is similar, but view models did not exist at this point yet:
Call for action:
In case you would like to see a rewrite of part 2 for neo.mjs v2.3, please add a comment here!
If you create your own neo.mjs app and write a blog post on this one, I would love to share it inside the blog. Just give me a ping :)
Looking forward to your feedback!
Best regards & happy coding,
Tobias
Preview image:
Recommend
-
10
pmem.ioPersistent Memory Programming Challenges of multi-threaded transactions Our library cur...
-
13
Asynchronous Multi-Threaded Parallel World of SwiftOver the last 15 years, CPU clock rate has plateaued due to thermal limitations in processors. As a result, CPU manufactures have instead chosen to add more cores or processing units...
-
9
Don't setenv in multi-threaded code on glibc Remember in 2014, when I wrote about getting stuck if you called basically anything between fork and exec if you have thread...
-
8
Multi-threaded unprotected accesses to static variables I think I've found yet another way to make things go really loopy in multi-threaded programs. Consider some C++ code which looks like this: static long...
-
11
Multi Threaded Debugging Hell Aug 21, 2016 As of late I've been dealing a lot with categorization issues and specifically with respect to failures in a categorization engine that I've written. What I was confronted wit...
-
6
Multi-threaded Camera Caffe Inferencing Jun 14, 2018 2019-05-16 update: I just added the Installing and Testing SSD Caffe on Jetson Nano post...
-
9
Clio: extremely fast, multi-threaded code on the browser Sep 20 ・7 min read
-
3
Multi-threaded SQLite without the OperationalErrors January 30, 2017 22:36 / peewee
-
5
[Last Week in .NET #85] – Multi-threaded Boards I asked the .NET Foundation when they were going to update us on their...
-
4
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK