Category Archives: JavaScript

Optimization Gotcha's for for/i and forEach

So I mentioned something on my interview with Alex of NativeScripting.com that he did with me. And someone asked about this in the comments, so I decided to create a blog article on this specific optimization tip.

I am going to code this to a browser rather than in NativeScript because well JS in a browser is just plain easier to show the memory issues because you can plop the code in to a new browser tab and step through the code instantly. ūüôā

<html>
<body><div id="stacklayout"></div></body>
<script type="application/javascript">

// Pretend Color class to be similar to NativeScript
class Color {
    constructor(val) { this._color = val; }
    toString() { return this._color; }
}

const <strong><em>colors </em></strong>= ["#ff0000", "#00ff00", "#0000ff"];
for (let i=0;i&lt;<strong><em>colors</em></strong>.length;i++) {
    const labels=["Wow","Awesome","Interview", "Alex", "Created!"];
    for (let j=0;j&lt;labels.length;j++) {
        const clr = new Color(<strong><em>colors</em></strong>[i]);
        const txt = <strong><em>document</em></strong>.createElement("div");
        txt.innerText = labels[j];
        txt.style.backgroundColor = clr.toString();
        const parent = <strong><em>document</em></strong>.getElementById("stacklayout");
        parent.appendChild(txt);
    }
}
&lt;/script>
&lt;/html>

And you should see something like this:

We are repeating the labels for each of the primary colors...

Or you might be used to doing something like this instead:

&lt;html>
&lt;body>&lt;div id="stacklayout">&lt;/div>&lt;/body>
&lt;script type="application/javascript">

class Color {
  constructor(val) { this._color = val; }
  toString() { return this._color; }
}

["#ff0000", "#00ff00", "#0000ff"].forEach((clr) => {
  ["Wow","Awesome","Interview", "Alex", "Created!"].forEach((word) => {
     const txtColor = new Color(clr);
     const txt = <strong><em>document</em></strong>.createElement("div");
     txt.innerText = word;
     txt.style.backgroundColor = txtColor.toString();
     const parent = <strong><em>document</em></strong>.getElementById("stacklayout");
     parent.appendChild(txt);
  });
});

&lt;/script>
&lt;/html>

Both have several issues, however the second one can be vastly worse than the first one depending on how many iterations. So lets walk through the issues.

So the Color class is fine, we are using it to show creating objects... So just ignore it for now.

const <strong><em>colors </em></strong>= ["#ff0000", "#00ff00", "#0000ff"];
for (let i=0;i&lt;<strong><em>colors</em></strong>.length;i++) {

These lines are fine, we allocated colors, then looped through them. So far so good. Lets proceed, what about:

const labels=["Wow","Awesome","Interview", "Alex", "Created!"];

Well here is the first issue; Fortunately for us, the v8 JS engine actually pre-allocates the 5 different string "Wow" through "Created!" when it parses the JavaScript however, each loop through it does create a brand new Labels array and then links those strings into it. So in this case it is small overhead; however in the 3 loops that still is 2 more sets of allocations and all new GC enties that have to be cleaned up after the loop is completed. If you were to put this single line of code outside the loop; your code runs the same, but no less memory usage and less processing.

 const clr = new Color(<strong><em>colors</em></strong>[i]);

What about this line, well in this case all 15 times it creates a brand new Color object. You can easily make this only be ran 3 times just by moving it outside the inner for (j) loop into the for (i) loop. In this example the Color class is very light weight. But many classes you create inside loops will be very heavy with lots of allocations going on when you allocate the class. So pay attention, 3 allocations vs 15 allocations, all by putting the line in the wrong spot...

Continuing on, most the rest of the lines has to be inside the loop; but I tossed another item into the loop, that wasn't needed... This line is very easy to accidentally end up in the loop because you needed it at that point.

     const parent = <strong><em>document</em></strong>.getElementById("stacklayout");

Yep, this has to do an expensive lookup in the dom to find this element, it never changes. It also has to allocate it to that "parent" variable 15 times. If you dropped it before/outside of both loops, you would have a single lookup and allocation...

---

Finally lets look at the last example; it suffers from the same issues. However, this one I used a Array.forEach rather than the simple for (i) loop. Why do I say it can be worse then? Well in small numbers, the difference is actually pretty tiny. The extra amount of memory it uses over a for (i) loop is also reasonable. But in larger loops (especially loops inside of loops inside of loops); the difference can end up being exponentially larger.

Your creating and calling functions, and all those functions now has added scopes and the engine has to setup all the callstacks for each and every iteration, additional error checking and just plain running a lot more code behind the scenes. So what is a simple for (i) loop in the first example, now became 15 EXTRA functions calls with all the extra overhead of cpu and memory that entails. Again in small chunks they are perfectly fine, but if you are wanting performance, every ms and gc adds up and sometimes they can add up very quickly when dealing with the Array helper functions that use callbacks...

As a better way to phrase this; imagine you have a for (i) loop that takes 5ms and a forEach loop takes 25ms to finish. Both are still dang fast, hard to even benchmark. But now you embed it in another forEach that also by itself takes 25ms. 25ms * 25ms = 625ms. More than half of a second. But, if you were to switch this to a for (i) loop each of them is 5ms. So, 5ms * 5ms = 25ms. Both are exponentially larger, but 25ms vs 625ms and the difference starts becoming much clearer. Now add a third loop; 5ms*5ms*5ms = 125ms. 25ms*25ms*25ms=15,625ms (or > 15 seconds!) In small chunks the time and memory used from a forEach is minuscule. But depending on how many times it is called; it can add up very quickly to be a major difference.

Please note this goes for all the nice Array helper functions, .map, .reduce, .filter, etc. Everyone of them that uses a callback, has somewhere between 2 times and 50 times as much overhead as a simple for (i) loop. Remember, despite this overhead; these functions are extremely fast. So don't be afraid to use them, but be more wary of using them in deeply nested code that can easily be called in loops when the datasets they are working against are also large.

As many items that you can actually move outside of your loop, the better off you are. This Includes any expensive calculations! You might be able to do 99% of the expensive calculation outside of the loop; and then finish off the last part of the calculation inside the loop...

I hope this answers the question about loops and performance. Every loop is a golden opportunity to greatly enhance your app,

NativeScript AndroidX support and Plugins

The next major version of NativeScript (version 6) will be switching from the android.support packages to the new androidx namespace. This will be a hard break; as you can only use the support api's or androidx api's; but not both. This is something Google has been implementing for a while; and NativeScript is getting on board to eliminate later issues and give us great support. The awesome developers at Progress have already created a fork of NativeScript; which you can install to test which uses androidx instead of using the support libraries.

Since one of my plugins is affected by the changes; I took a look into what was required to support both the current versions of NativeScript and the new upcoming version of NativeScript.

For any of you who have plugins; this code is what I basically devised.

let androidSupport=null;
if (android.support && android.support.v4) {
  androidSupport = android.support.v4;
} else if (global.androidx && global.androidx.core) {
  androidSupport = global.androidx.core;
}

Then you can use androidSupport to access the namespace, and it pretty much works the same as what you would access using android.support.v4 or the new androidx.core replacement. Because we used feature detection; this allows us to support both namespaces in the same code base.

So if you have JavaScript or TypeScript code that uses android.support.* then you can use this technique to make your plugin forwards compatible with the next major version of NativeScript while retaining support for the prior versions.

Here is a list of classses that are being moved from android support to androidx: https://developer.android.com/jetpack/androidx/migrate

NativeScript 5.1.0 Released

For those who upgraded to 5.0.0 last month, it was a bit of a rough ride.  A lot of cool features but a lot of weird corner case broken items.  Fortunately since that point, they have released a several 5.0.x point releases which fixed several of the larger flaws.  5.1.0 actually fixes several of the non-critical smaller flaws and adds some cool new features...   So if you were waiting to jump on the 5.x bandwagon, this should be the stable release you are waiting for!

The quick list of some of the new features in 5.1

Core Modules

Enable modal dialog chaining in IOS - this allows you to have another dialog follow the first; anyone who has tried this in the past; know this was always a pain on iOS.

isScrollEnabled - This allows you to disable scrolling in the scrollbar component.

androidSwipeEnabled - Allows you to disable swiping in the Android tabview control.

You can use the arrow keys to select an option, in this screen shot; the "Plain JavaScript" is chosen and so it is highlighted.

Android

Android AAB (Android App Bundle); support added!

New package.json flag;  suppressCallJSMethodExceptions: true/false - enables suppression of the boolean errors when calling a native function.  This could happen and crash the app.  Now you can suppress them.  (see http://fluentreports.com/blog/?p=581 for more information) 

Java 8 Static method support on interfaces; allows NativeScript to call these Static functions.

extends should now work against standard javascript classes (i.e. non native Android classes); so that you can now do class MyVue extends Vue {}; class blahComp extends MyView {}; and it will work properly.

iOS

CStrings are a bit more resilient when passing to a function that wants them as a pointer to read and possibly write.

Updated JavaScript engine to JSC 12.0

CLI

Several bugs when using hmr are fixed.

Yarn Support

Adding lots more Analytics/Tracking (Don't forget to disable this for your privacy: tns usage-reporting disable && tns error-reporting disable)


Updating NativeScript

To get updated; you first need to do:
npm i -g nativescript@latest

That will get you the latest version of NativeScript CLI; once you have it; do a "tns --version" and verify it prints out "5.1.x".  Then do a "tns doctor" to verify your environment is up to date and has all the newest support tools you need for a successful build.  

To update a project; you need to do the following:

Latest Runtimes:
tns platform remove android && tns platform add android@latest
tns platform remove ios && tns platform add ios@latest

Latest Core modules:
npm r tns-core-modules --save
npm i tns-core-modules@latest --save

To install Webpack & HMR support:
npm i nativescript-dev-webpack@latest --save-dev
Note: you need to have nativescript-dev-webpack as a development dependency for HMR to work.  

To install latest NativeScript Angular plugin
npm i nativescript-angular@latest --save
You will then need to install the actual angular bits; which as of this post v6 is currently supported.

The addition of all the additional analytics/tracking to the CLI reminded me; you can disable it permanently; if you value your privacy by doing:
tns usage-reporting disable && tns error-reporting disable


Known issues

NativeScript 5.0 Released

It has been a long road getting to this point; but I'm happy to see that the NativeScript team released The final piece of the 5.0.  The CLI tool which was the final piece was finally released on Nov 2nd; the rest of the pieces have been released for a couple days.

5.0 adds a whole lot of new awesome features; however since several pieces have changed; I would highly recommend before uploading a 5.0 app to the app stores you do another full test of your application; testing everything as several low level framework items changed for both iOS and Android.

The quick list of some of the new features in 5.0

CLI Prompts

The cli now will prompt you for input; so when you type "tns create ProjectName" you will see something like this:

You can use the arrow keys to select an option, in this screen shot; the "Plain JavaScript" is chosen and so it is highlighted.

HMR - Hot Module Replacement

This is the feature most NativeScript developers have been waiting for; this is awesome.  You know the silly change a file app restarts; change another file app restarts livesync?    Now you can do this:  tns run --hmr

HMR, allows the page and JavaScript code to be updated dynamically (no more full app restarts) ; if you change a class the class may have to be re-instantiated (like in this example), but if you change other things, like functions, the state is preserved and the new function will be what is called the next time that function is needed.   All in all, this is very HOT!   (You do need to have the nativescript-dev-webpack as a developer dependency of your project for HMR support)

NativeScript Preview

QR Code

Preview allows you to preview the application without having xcode or android tools installed and building it.  It uses the already built playground preview application to preview the app so that you can see the app on your mobile phone.   It has the same limitations as the playground site; but for most developers getting started it is a quick and easy way to start playing around with programming application.    When you type:
tns preview

you will then see the qr code in your console.  Point the playground app's qr scanner at it and then your app will start running on your phone.   Please note the entire application is uploaded to the cloud; which then your phone links to.  So if this is an issue with your company  don't use this feature.

Internal Changes

Android Core modules is now descended from the Google Support libraries; this opens up many more visual plugins and makes the app a lot more modern and compatible.   One word of caution this did require them to do a bit of reworking of several parts of the base code for android.  Their is the likely potential for new bugs to appears in areas that were bug free before.  So heavily test your app before releasing it to the stores.

iOS Core modules now have a new way to do the safe areas; this also has required a lot of reworking and so the same warning applies.  Test your app thoroughly to make sure that something in this area won't cause you any issues.  

Other android specific changes:

  • now using the v8 engine, v6.9.427.23.
  • before-plugins.gradle support
  • SBG will fail if their is a JSParser issue
  • JSParser fix for Decorators
  • Runtime issue fix with support > 26 on devices < 23.
  • Gradle is now using 3.2
  • Building is even faster now

Other iOS specific changes:

  • Typing generation improvements
  • Webkit is now using 11.4
  • Several marshalling to native from JS issues fixed.

Other specific changes:

  • Many bugs fixed
  • Webpack Vue  bundling support
  • Angular memory leak fixed
  • Styling changes to support dynamic theming
  • tslib helpers now global
  • appSettings now supports allKeys method
  • Touch event pass through now supported


Updating NativeScript

To get updated; you first need to do:
npm i -g nativescript@latest

That will get you the latest version of NativeScript CLI; once you have it; do a "tns --version" and verify it prints out "5.x.x".  Then do a "tns doctor" to verify your environment is up to date and has all the newest support tools you need for a successful build.  

To update a project; you need to do the following:

Latest Runtimes:
tns platform remove android && tns platform add android@latest
tns platform remove ios && tns platform add ios@latest

Latest Core modules:
npm r tns-core-modules --save
npm i tns-core-modules@latest --save

To install Webpack & HMR support:
npm i nativescript-dev-webpack@latest --save-dev
Note: you need to have nativescript-dev-webpack as a development dependency for HMR to work.  

To install latest NativeScript Angular plugin
npm i nativescript-angular@latest --save
You will then need to install the actual angular bits; which as of this post v6 is currently supported.


Known issues

NativeScript/JavaScript Tip: Debugging

Once thing I was made aware of today was that not everyone knows the magic "debugger;" command.   I suppose this makes sense; I rarely see it mentioned, but it is a very useful debugging command in the JavaScript world.

The "debugger;" command is used to cause the application to stop and enter the debugger at that point.   This works in Node.js, the browsers, and even in NativeScript!    In a browser the developer tools must be open; if your debugger tools are closed; it will ignore it.     In node, a debugger must already be attached or it will also ignore it.

In NativeScript it will actually halt the program and WAIT until a debugger is attached!   So don't release your code

So pretend my code in main-page.js is

function pageLoaded(args) {
var page = args.object;
debugger;
// do something with the page object.
}

You can then start the application on the device; and it will automatically stop right at that debugger; statement.   Then at the command line type: tns run android --start and you will be in the debugger right at that line.

NativeScript: Keeping your AndroidManifest.xml easily editable and under version control.

So here I was again minding my own business on the forums again, and Ben posted he had been attempting to use this new feature of v1.5.x of NativeScript and it was failing.  I responded it didn't work like that.   Thankfully he was persistent and ignored my wrong response, and linked to the github issue a second time where they discuss the new feature.   Him, being persistent despite me telling him it doesn't work that way; is why we now have a way to make it work!   So thank you Ben!

So I went and looked at the new feature and found that by default it doesn't work as a normal person would expect.    It does exactly what I expected and why I said it doesn't work.    However, I decided to see if I could make it work the way any sane person would want it to and guess what, I have a method to make this work perfectly.

To make this work properly;  you need NativeScript v1.5.1 or later; so type tns --version and make sure you are running v1.5.1 and later, if not upgrade.

WARNING: changing things in your platforms folder can completely break your build; and if you totally break your build you can do a tns platform remove android and then a tns platform add android to reset it.

First, you will need to navigate to your /platforms/android/src/main and copy the AndroidManifest.xml file to your /app/App_Resources/Android/ folder.   This is going to be your primary manifest and the one you will edit to your hearts desire.  Make sure you COPY just your app manifest.

Next you will need to edit the /platforms/android/src/main/AndroidManifest.xml and basically deleted everything in it.   This file should end up looking like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest  xmlns:android="http://schemas.android.com/apk/res/android"
package="YOUR_APP_PACKAGE" >
</manifest>

Do not copy the above as is; you need to make sure the "package" property contains your app package name, which is why I recommend you edit the one in their as it already has your app package in it.  If you copy the example above, it has YOUR_APP_PACKAGE which is an invalid package name.

Because the manifests are merged; and since the package name doesn't change the merging system is able to merge everything in your brand new App_Resources/Android/AndroidManifest.xml into the original AndroidManifest.xml file without any conflicts!

 

 

Getting Started with NativeScript - On Sale Now!

Getting Started with NativeScript

Getting Started with NativeScript

WooHoo!   It has been such a long road to get this book published.   I wrote a bit about it here.  It is finally  officially available on the Packt site for sale today!   It should be available on the Amazon store by Monday the 1st of February.  I will have a list of additional sites and urls later this week.  If you want the best book on NativeScript (I know I'm so funny!  I get to claim it because its the only book on NativeScript!) then it is now available!

The available sites are as follows: