Category Archives: JavaScript

How to handle Button clicks in React Native – Part I

I’ve been building an app which lets me find movies “near me” using mocked data. In my last post, I added a progress loader to display when there’s networking latency. I have a dropdown with a list of movie names in my app, and the user can select any movie in the list. Let’s suppose the user wants to see one of these movies, and would like to see a list of closest locations where the movie is showing. We’re going to need a button so that the user can click it to get results.

You might wonder why you shouldn’t just load results when the user selects a movie name from the Picker. Well, it depends on how you feel about UX. Some apps will immediately give you results when an element is selected from a dropdown. However, just because a user selects an item doesn’t mean that is their final choice. Maybe they lifted their finger too soon, and wound up selecting something that they didn’t want. Or, maybe they’re just thinking about their different options.

My personal preference for dealing with a select is to add a confirmation button when the result of selecting an item from the Picker is a resource intensive task, like hitting a database or making a network call. You don’t want to waste resources every time a user selects an item from a dropdown.

So in this example, making a network call should only happen if the user clicks a button. Let’s add a button to our view – just below the Picker:

import { View, Picker, ActivityIndicator, Button } from 'react-native';
...
render() {
    ...
    return <View>
        <Picker...
        </Picker>
        <Button title="Find Movie Near Me"></Button>
    </View>;
    ...
}

You can’t add a Button without a title; you’ll see a warning if you do.

The button is probably the easiest control to add in React Native! Here’s what I see after I add just a couple of lines:

However, clicking the button is a different story. If you’re used to web development, you might expect an “onClick” property. Nope, it’s onPress. And you can’t just add a method like this:

...
handleClick() {
    console.log("Handled");
}
...
    <Button onPress="handleClick" title="Find Movie Near Me"></Button>
...

The app won’t complain if you do this, but if you click the button you’ll see an error: "TypeError: this.props.onPress is not a function. (In 'this.props.onPress(e);, 'this.props.onPress' is "handleClick")".

The method handleClick is actually a function of “this”, the app. So you have to reference it correctly. It doesn’t help to replace onPress="handleClick" with onPress="this.handleClick" because anything in quotes is a “literal”, treated as a string, in JSX. Instead, you have to do as follows:

...
handleClick() {
    console.log("handleClick");
}
...
    <Button onPress={this.handleClick} title="Find Movie Near Me"></Button>
...

Now the button responds to clicks, as you can see below!

Got comments? Write an email to me at fullstackdev@fullstackoasis.com and click the send button! If you found this interesting, click the subscribe button above. I write a new post about once a week.

How to add a progress loader in React Native

In my last post, I took a look at how network latency might affect my React Native app, and I didn’t like what I saw: a pure white screen for several seconds. How is a user supposed to “react” to that? (Pun intended!)

It was pretty clear what was happening. My app’s render method just shows a blank view if it has no state. Here’s the render method:

render() {
    if (this.state && !this.state.isLoading) {
        ...
    } else {
        return (<View></View>);
    }
}

That’s my white screen. There’s a super simple fix:

render() {
    if (this.state && !this.state.isLoading) {
        ...
    } else {
        return (<View><Text>waiting...</Text></View>);
    }
}

Now there’s text which reads “waiting…” at the top. That’s okay for a hobby app. You’re probably going to want that to look a little nicer, however. How about a progress indicator? Something that shows “we’re working on this!”. React Native comes with an ActivityIndicator which does what we want. I’m going to grab some of the code from their documentation and add it to my app. In the render method below, I’ve replaced my “waiting” text with an ActivityIndicator (and I’ve shown the import statement to remind you to add that as well).

import { View, Text, Picker, ActivityIndicator } from 'react-native';
...
render() {
    if (this.state && !this.state.isLoading) {
        ...
    } else {
        return (<View><ActivityIndicator size="large" color="#0000ff" /></View>);
    }
}

That helps some, but the progress indicator still doesn’t look very nice. It’s placed at the very top of the screen, like this:

It turns out to be pretty easy to move the ActivityIndicator to the center of the screen. You just have to add some style components to its container View, like this:

render() {
    if (this.state && !this.state.isLoading) {
        ...
    } else {
        return (<View style={[{ flex: 1, justifyContent: 'center' },
            { flexDirection: 'row', justifyContent: 'space-around', padding: 10 }]}>
            <ActivityIndicator size="large" color="#0000ff" />
        </View>);
    }
}

Here’s the result:

With very little trouble at all, I’ve now got a slick little widget to let people know that something is going on. It’s not really enough, though. What if the network request times out, and I get an error? I’ll have to delve into that more deeply later. To remind me to do this, I’ll add a TODO/FIXME comment in the code, and open an issue in my issue tracker.

Got comments? Send them to me in an email at fullstackdev@fullstackoasis.com. If you found this interesting, go ahead and click the subscribe button above. I write a new post about once a week.

What causes “SyntaxError: JSON Parse error: Unrecognized token ‘<'" in React Native?

TL;DR: In React Native, you will get “SyntaxError: JSON Parse error: Unrecognized token ‘<‘” if your URL returns a 404 error, or in general if the content is not a JSON string.

In a recent post, I showed how to display a list of movies that had been fetched from a REST API. It worked great, but I wondered what would happen to my app’s user if their device was offline, or if the REST API ever went down. To mimic this behavior, I changed the URL by adding the number 1 at the end of it, like this: “https://facebook.github.io/react-native/movies.json1”.

And here’s what I saw in the emulator:

SyntaxError: JSON Parse error: Unrecognized token ‘<‘

The red screen says “SyntaxError: JSON Parse error: Unrecognized token ‘<‘”. That may be confusing, although if you work with REST APIs for any time, you’ll soon come to recognize what it means. Meantime, how do we investigate this?

When I load up this test URL in a web browser, I see content which looks like this:

<!DOCTYPE html>
<html>
  <head>
  ...Page not found...
</html>

It’s a fancy 404 error page. That explains why response.json barfs on this; it’s not JSON. Your app expected a JSON string. It tried to parse the string into a JavaScript object, and couldn’t handle a non-JSON string. As a reminder, here’s that fetch call:

componentDidMount() {
    return fetch('https://facebook.github.io/react-native/movies.json1')
        .then((response) => response.json())
        .then((responseJson) => {
...
        })
        .catch((error) => {
            // TODO FIXME replace the red screen with something informative.
            console.error(error);
        });
}

In the longer term, I will want to replace that red screen of death with a nice error page which instructs the user what to do. I’m still developing my application, however, and as a dev, I’d rather see the stack trace for errors like this when they occur.

So to deal with this, I’ll do two things: 1) I’ll add a “TODO FIXME” note in my code. When I’m cleaning up code in the end stages of development, I know to look for these types of comments which indicate work still needs to be done. 2) I’ll open an issue in my issue tracker which will let everyone on my team know that there’s something that still has to be handled in building the application. I’ll bring this to the attention of anyone who needs to know (a project manager, perhaps). The project manager may assign a designer to build a page with some graphics or specific text to display to the user in case of this error.

Got comments? Send them to me in an email at fullstackdev@fullstackoasis.com. If you found this interesting, go ahead and click the subscribe button above. I write a new post about once a week.

How to add a select element in React Native

TL;DR: The React Native Picker component is the equivalent of the HTML select element.

In my last post, I used fetch to download a list of movie titles. However, I only displayed the list by using a debugging tool. My app user will want to see this list, and even better, may want to select something off the list. The commonly used web widget which does this is the HTML <select> element. In React Native, the corresponding widget is a Picker.

You can add a Picker to your render method very easily, like this:

import { Picker } from 'react-native';
...
render() {
    return <View>
        <Picker>
        </Picker>
    </View>;
}

That’s a start, but it doesn’t display my data, and it doesn’t look very interesting. We’ve got a picker with no functionality which looks like this:

Notice the little upside-down triangle in the upper right corner, which indicates the Picker has loaded.

In order to populate the Picker with data, we need to get access to the list of movies inside the render method. Fortunately, I already used setState to make the movie data persist in my app’s state (see previous post). That means I can use it in render, like this:

render() {
    let items = [];
    var length = this.state.dataSource.length;
    for (var i = 0; i < length; i++) {
        var item = this.state.dataSource[i];
        // Really, really important to not put quotes around these braces:
        items.push(<Picker.Item label={item.title} value={item.title} key={item.id} />);
    }
    return <View>
        <Picker>
        </Picker>
    </View>;
}

Uh oh, wait, as soon as I added this code, I saw the red screen of death in the emulator. The error message read “TypeError: null is not an object (evaluating ‘this.state.dataSource’). This error is located at: in HelloWorldApp (at renderApplication.js:40)…”

The problem is that I tried to reference this.state.dataSource before it had been set. The render method might be called before fetch runs. We don’t know when our fetch statement will be called, or even if it is successful. We need to add code in render which handles the case where this data is not yet available.

Here’s the new render code which skips doing anything with dataSource if it is not found:

render() {
    if (this.state && !this.state.isLoading) {
        let items = [];
        var length = this.state.dataSource.length;
        for (var i = 0; i < length; i++) {
            var item = this.state.dataSource[i];
            items.push(<Picker.Item label={item.title} value={item.title} key={item.id} />);
        }
        return <View>
            <Picker>
            </Picker>
        </View>;
    } else {
        return ( <View></View>);
    }
}

That fixes the error, but the Picker is still empty. But in anticipation of needing a list of movies, I’ve already got a list of Picker.Item objects loaded into an array using JSX (items.push(<Picker.Item....). It turns out to be very easy to add these to the Picker. Just add items in braces inside the Picker tags, like this:

<Picker>
{items}
</Picker>

As soon as this is done, like magic, the Picker reflects the changes. A dropdown appears with the text Star Wars selected by default.

Now you can click the dropdown, and try to select a different movie, but what you’ll find is that the dropdown is stuck at Star Wars – you can’t select anything else!

For a finishing touch, let’s make it possible for the user to select a different movie. The Picker documentation tells us how to do this by adding the selectedValue attribute and the onValueChange methods like this:

selectedValue={this.state.movie}
onValueChange={(itemValue, itemIndex) =>
    this.setState({ movie: itemValue })
}

After adding this I can select any movie, and that movie stays selected! Here’s my final code:

import React, { Component } from 'react';
import { View, Picker } from 'react-native';

export default class HelloWorldApp extends Component {
    componentDidMount() {
        return fetch('https://facebook.github.io/react-native/movies.json')
            .then((response) => response.json())
            .then((responseJson) => {
                console.log(responseJson);
                this.setState({
                    isLoading: false,
                    dataSource: responseJson.movies,
                }, function () {

                });

            })
            .catch((error) => {
                console.error(error);
            });
    }

    render() {
        if (this.state && !this.state.isLoading) {
            let items = [];
            var length = this.state.dataSource.length;
            for (var i = 0; i < length; i++) {
                var item = this.state.dataSource[i];
                // Really, really important to not put quotes around these braces:
                items.push(<Picker.Item label={item.title} value={item.title} key={item.id} />);
            }
            return <View>
                <Picker selectedValue={this.state.movie}
                    onValueChange={(itemValue, itemIndex) =>
                        this.setState({ movie: itemValue })
                    }>
                    {items}
                </Picker>
            </View>;
        } else {
            return (<View></View>);
        }
    }
}

Got comments? Send them to me in an email at fullstackdev@fullstackoasis.com. If you found this interesting, go ahead and click the subscribe button above. I write a new post about once a week.

Simple debugging tool in React Native

TL;DR: If you want to debug React Native code really quickly, console.log and console.warn can help.

In my previous post, I described how I ported the React Clock app to React Native. This is the code for my simple app:

import React, { Component } from 'react';
import { Text, View, Button } from 'react-native';
import Clock from './Clock';

export default class HelloWorldApp extends Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
                <Text style={{ fontWeight: 'bold', padding: 10 }}>Hello, world!</Text>
                {/* padding does not work with Button!! */}
                <Button style={{ fontWeight: 'bold', padding: 40 }} title="Click me" >Click Me!</Button>
                {/* Since padding does not work with Button, we add an empty text area */}
                <Text>{""}</Text>
                <Clock />
            </View>
        );
    }
}

For my next project, I decided to do something more realistic. I wanted to figure out how to fetch data from a REST API.

I already had the Android emulator started (see previous post). A quick look at the React-Native networking documentation told me that doing a fetch should be a piece of cake. Because I didn’t want to copy their entire sample app, but just reuse their fetch call, I copied the componentDidMount method, and pasted it into my application above the render method:

componentDidMount(){
return fetch('https://facebook.github.io/react-native/movies.json')
    .then((response) => response.json())
    .then((responseJson) => {

    this.setState({
        isLoading: false,
        dataSource: responseJson.movies,
    }, function(){

    });

    })
    .catch((error) =>{
    console.error(error);
    });
}

(The componentDidMount method may be familiar to you from React.js development.)

I didn’t see any errors when I did this, but I also couldn’t tell whether the fetch method had worked! If I had been building this app using JavaScript in a web browser, I could have quickly checked the results by adding a console.log statement to print out responseJson. I tried this, in fact, but nothing noticeable happened onscreen when I made my change. It took a little while, but I finally noticed that my statements were being logged in the terminal window that was running the Metro server (where I’d run the npm start command)! It took me a while before I noticed this because I’m not usually looking at the terminal unless I’m trying to debug a problem.

A quick search also told me that I could use console.warn to display text on the emulator’s screen. I added console.warn(responseJson); just above the setState call, and I could see that the method had succeeded, and I could also see part of the responseJson content in the YellowBox which appeared. Clicking on this YellowBox warning gave me a fullscreen view of the JSON.

Probably it’s a bad idea to display debug messages using console.warn, but if I were debugging on a device without the help of Metro server, I think console.warn would come in handy.

Got comments? Send them to me in an email at fullstackdev@fullstackoasis.com. If you found this interesting, go ahead and click the subscribe button above. I write a new post about once a week.

Invariant violation: Text strings must be rendered within a Text component

TL;DR: To display text in React Native, you must always use a Text tag. This is different from React.js, where text can be displayed in html tags like <div> or <h1>.

For my next mini React Native project, I’m porting the React Clock app to React Native. FYI I’m doing this work on Ubuntu 16.04.

I have to start up my Android emulator first. I list the available ones like this:

emulator -list-avds
Galaxy_Nexus_API_23
Galaxy_Nexus_API_28
Samsung_S4_mIni_API_17

I pick one, and start it:

emulator -avd Galaxy_Nexus_API_28

I didn’t want to have to fire up Android Studio every time I worked on a React Native project, so running the emulator from the command line is super useful. Note the emulator has nothing to do with React Native, per se, and it’s just running in a standalone mode. I can run my emulator commands from any directory; I just open a terminal and do it.

I had already gotten started with React Native. I had a folder called AwesomeProject with a directory structure like this:

...
ios/
android/
App.js

I step into this directory and run the commands npx react-native run-android followed by npm start. I see my project load into the emulator.

I’m using VS Code as my IDE for React Native development. I have no trouble making a small change or two to my App.js file, and I see these changes immediately loaded into the emulator.

Next, I add a file called Clock.js in the same directory as my App.js class.

import React from 'react';
export default class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

This is almost the same as the Clock.js code in the React.js state and lifecycle demo. I’ve removed the ReactDOM.render method because I don’t want the clock rendering immediately. Also, I’ve exported the class so it can be used by other components.

Now I add the Clock to my App. There’s just one line to import it:

import Clock from './Clock';

And a tag to add it:

<Clock/>

This is what my App.js file looks like:

import React, { Component } from 'react';
import { Text, View, Button } from 'react-native';
import Clock from './Clock';

export default class HelloWorldApp extends Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
            <Text style={{ fontWeight: 'bold', padding: 10 }}>Hello, world!</Text>
            {/* padding does not work with Button!! */}
            <Button style={{ fontWeight: 'bold', padding: 40 }} title="Click me" >Click Me!</Button>
            {/* Since padding does not work with Button, we add an empty text area */}
            <Text>{""}</Text>
            <Clock/>
            </View>
        );
    }
}

As soon as I did this, I saw an error message in my emulator:

The error message reads “Invariant violation: Text strings must be rendered within a component. This error is located at: in h1 (at Clock.js:28) in dev (at Clock.js:27)…”

Since the location of the error is very nicely displayed, I can quickly find the problem. The render method of my React.js Clock class is

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );

and the complaint is about the text “Hello, world!” within the <h1> tags. It didn’t take long before I discovered the problem in the React Native documentation for the Text component: “In React Native, we are more strict about it: you must wrap all the text nodes inside of a component. You cannot have a text node directly under a <View>.”

Let me just try using a Text tag instead of h1 and h2 tags. First I add the import statement:

import { Text } from 'react-native';

And then I swap out <h1> and <h2> for <Text>. That seems to work, but now I have a new error: “Invariant violation: View config not found for name div. Make sure to start component names with a capital letter.”

I already know the complaint is about the div tag in Clock.js. Whoops, divs do not live in native apps. I’ll just change my div tag to a Text tag and see what happens. Here’s my render code:

  render() {
    return (
        <Text>
            <Text>Hello, world!</Text>
            <Text>It is {this.state.date.toLocaleTimeString()}.</Text>
        </Text>
    );
  }

And here’s my running clock!

Got comments? Send them to me in an email at fullstackdev@fullstackoasis.com. If you found this interesting, go ahead and click the subscribe button above. I write a new post about once a week.

PHP in_array or array_search vs JavaScript includes or indexOf

In my previous post, I’d created a bug in my PHP code by improper use of PHP’s array_search function. If you only write PHP, this kind of thing will probably never happen to you. I also write Java and JavaScript code, and at some point, your memory banks overflow, and you forget stuff!

JavaScript has a couple of different methods for searching through an array to find an item. Let’s use the array method includes to search for an item in JavaScript, just like I did in my PHP example.

var foods = new Array();
foods.push('ice cream');
foods.push('hamburger');
foods.push('brussels sprouts');

var do_important_things = function() { console.log('Important!'); };

if (foods.includes('ice cream')) {
    do_important_things();
}
...

If you print out foods, you can verify that ‘ice cream’ is located at index 0. The code foods.includes('ice cream') returns true because that string is found in the array. No confusion there – a boolean is returned, not an index.

If I’m doing a more complicated comparison, I might use JavaScript’s some, like this:

if (foods.some(function(el) { return el.toLowerCase() === 'ice cream'; })) {
    do_important_things();
}

The some method loops over all elements in the array until the callback method returns true, at which point it stops, and returns true. If no item is found which satisfies the condition in the callback, then false is returned. Again, since a boolean is returned and not an index, there’s no room for error.

There’s a JavaScript method which is the equivalent of the PHP array_search function: indexOf. It returns the index of the matching item, if found, and otherwise returns -1. Here’s an example:

if (foods.indexOf('ice cream')) {
    do_important_things();
}

Oops, this method of searching exhibits the same bug as array_search in PHP! In my opinion, the indexOf JavaScript method is more appropriately named – it makes it clear that you are going to get an index as a return value. So it makes it less likely that the all-too-human developer will create the bug that I demonstrated in my previous post.

Now, when researching this topic, I found that PHP has a function that is more appropriate than array_search for my use case. It’s the in_array function. It returns TRUE if the item being sought is found in the input array. That’s really what I wanted! Here’s the example code that I used in my previous post, only now it uses in_array:

$foods = Array();
$foods[] = 'ice cream';
$foods[] = 'hamburger';
$foods[] = 'brussels sprouts';

function do_important_things() {
    echo 'Important!';
};

if (in_array('ice cream', $foods)) {
    do_important_things();
}

In this example, do_important_things is called.

Got comments? Send me an email at fullstackdev@fullstackoasis.com. If you found this interesting, you might want to hit the subscribe button above. I write a new post about once a week.

About those JavaScript performance tests

In my last couple of articles, I wrote about some simple performance tests to compare Array.some with a for loop. I noticed that the for loop ran substantially faster the second time it was called in my process, while Array.some did not.

This got me curious. I wanted to know what was happening, so I dug around a little bit. It’s the kind of behavior that can be pretty tough to track down. I started with freenode for #Node.js. I described what I’d seen and got some feedback. I didn’t find a definitive answer, although I did get a clue. I was told essentially that “microbenchmarking” is pointless, and one should just write maintainable code, and let the JIT compiler worry about performance. I was directed to Mr Aleph‘s website. Mr Aleph is a compiler engineer and enthusiast, and I found a lot of interesting articles at his site.

In 2012 already, he had written a microbenchmarks fairy tale. It turns out that the V8 JavaScript engine compiler performs various optimizations that can produce misleading benchmarks. As an example, he talks about LICM (“loop-invariant code motion”). Oh god, the jargon! But he gives an example, which I’ll explain now.

Prior to working with JavaScript, I was a Java coder. In Java, one commonly writes for loops like this: for (int i = 0; i < arr.length; i++) {...}.

When I was getting started using JavaScript, I was told to assign Array.length to a variable outside the loop, and then reference the variable in the iteration, like this:

var len = arr.length;
for (var i = 0; i < len; i++) {
    ...
}

The reason given was that JavaScript is an interpreted language, and it will evaluate Array.length on every iteration over the loop! Sounds wasteful to me. So I’ve been dutifully, manually setting a variable to Array.length outside my loop ever since then.

Java developers don’t need to do this, because Java is a compiled language. The compiler performs optimizations behind the scenes which basically do what I was doing manually in JavaScript.

Flash forward to 2008-ish, about 10 years ago, when Google Chrome was launched, integrated with the V8 engine. If you thought I’d gotten off-track, I’m now back on it. According to Mr. Aleph, the V8 engine uses LICM, and LICM performs the optimization I’ve been doing manually in assigning Array.length to a variable outside the loop. And that’s just the tip of the optimization iceberg.

If you want to read more about the V8 engine, there’s an excellent series about it written by Alexander Zlatkov in 2017. Start with How JavaScript works: inside the V8 engine + 5 tips on how to write optimized code. You should also take a good look at Mr. Aleph‘s articles.

Once JavaScript is not strictly being interpreted, trying to benchmark and optimize your code manually is probably just a mistake. Mr. Aleph’s conclusion, in 2012, was

The time when you could have easily guessed the cost of a single operation by simple observation is over. If you want to know something then sometimes the only way is to go and actually learn it from the inside out.

Mr. Aleph, “microbenchmarks fairy tale“, 2012

My own conclusion is that you should not think you are going to outsmart an optimizing compiler. Just write code which is readable. If, at some point, you notice bottlenecks, that’s when it makes sense to dig for performance improvements.

But I’ll still move Array.length outside of my iterators 😉

A performance test of the JavaScript Array.some function in Chrome browser

Previously, I performance-tested the Array.some function in Node.js version 8.16. Node.js is usually used for running microservices as a backend on a server. It’s not identical to JavaScript running client-side, in the browser. So it’s also interesting to run the same kind of performance test in a browser to see if the results apply there, too.

The source code for this test is similar to what it was in my previous test using Node.js. There are some necessary differences. I wanted to use the same word list, but I didn’t want to fiddle with trying to get the browser to read from a file, which is complex at best and impossible at worst (for security reasons).

Instead, I decided to use XMLHttpRequest to grab the word list I’d used previously, which was handily available on a web page.

However, you can’t just open Chrome browser and run the code below. If you try it (and you might want to), you’ll see this error message:

Access to XMLHttpRequest at 'https://norvig.com/ngrams/count_1w100k.txt' from origin 'chrome-search://local-ntp' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Oops! The solution is to first surf to Norvig’s page, then open the JavaScript console with Ctrl-Shift-J, paste in the code below, and call runTest().

/* Displays results */
var finishAndPrint = function(words) {
    console.log("words length: " + words.length);
    console.log("First word: " + words[0]);
    console.log("Last word: " + words[words.length-1]);
};

/* This test function is guaranteed to never match a word in the Norvig list of words */
var testFunction = function(item) {
    return item === "gsdjkoeu";
};

/* A performance test using the Array.some method: */
var performanceTestSome = function(words) {
    let t0 = performance.now();
    let result = words.some(function(el, i) {
        return testFunction(el);
    });
    let t1 = performance.now();
    console.log("The some method took " + (t1 - t0) + " milliseconds and the result was " + result + ".");
};

/* A performance test using a simple for loop: */
var performanceTestForLoop = function(words) {
    let len = words.length;
    var result = false;
    let t0 = performance.now();
    for (var i = 0; i < len; i++) {
        if (testFunction(words[i])) {
            // This code is necessary so the function does the same thing as the "some" method
            result = true;
            break;
        }
    }
    let t1 = performance.now();
    console.log("The for loop method took " + (t1 - t0) + " milliseconds and the result was " + result + ".");
};

/* Read words from a URL and return the list of words */
var readTheWordsFromURL = function() {
    let myURL = "https://norvig.com/ngrams/count_1w100k.txt";
    var xmlHttp = null;
    xmlHttp = new XMLHttpRequest();
    xmlHttp.open( "GET", myURL, false );
    xmlHttp.send( null );
    return xmlHttp.responseText;
};

var runTest = function() {
    var data = readTheWordsFromURL();
    var words = [];
    var lines = data.split('\n');
    lines.forEach(function(row, i) {
        var items = row.split('\t');
        if (items[0].length > 0) {
            words.push(items[0]);
        }
    });
    finishAndPrint(words);
    performanceTestForLoop(words);
    performanceTestForLoop(words);
    performanceTestSome(words);
    performanceTestSome(words);
};

runTest();

I did this in Chrome browser Version 75.0.3770.90 (Official Build) (64-bit), on my Ubuntu 16.04 desktop. I used private mode to make sure no other tabs, plugins, etc. would interfere with the test.

These were the results the first time I ran the code:

words length: 100000
VM18:4 First word: THE
VM18:5 Last word: PGY
VM18:36 The for loop method took 2.8300000121816993 milliseconds and the result was false.
VM18:36 The for loop method took 0.5550000059884042 milliseconds and the result was false.
VM18:20 The some method took 3.064999997150153 milliseconds and the result was false.
VM18:20 The some method took 2.6000000070780516 milliseconds and the result was false.

I repeated the test (closed the browser, reopened it, and ran the test), but this time changed the order of the tests. I ran the some test first:

...
VM18:20 The some method took 3.5149999894201756 milliseconds and the result was false.
VM18:20 The some method took 2.5599999935366213 milliseconds and the result was false.
VM18:36 The for loop method took 2.599999977974221 milliseconds and the result was false.
VM18:36 The for loop method took 0.670000008540228 milliseconds and the result was false.

Huh! I tried it again:

...
VM18:20 The some method took 3.5499999939929694 milliseconds and the result was false.
VM18:20 The some method took 2.634999982547015 milliseconds and the result was false.
VM18:36 The for loop method took 2.449999999953434 milliseconds and the result was false.
VM18:36 The for loop method took 0.5299999902490526 milliseconds and the result was false.

And again:

...
VM18:36 The for loop method took 2.9750000103376806 milliseconds and the result was false.
VM18:36 The for loop method took 0.5549999768845737 milliseconds and the result was false.
VM18:20 The some method took 3.939999995054677 milliseconds and the result was false.
VM18:20 The some method took 2.789999998640269 milliseconds and the result was false.

I ran another test case, just running the some method only. The result took 3.429999982472509 milliseconds. A test with just the for loop running once took 2.905000001192093 milliseconds.

The only conclusion I can draw is that Chrome doesn’t quite perform as well when running some instead of a simple for loop. The difference is not huge, and there’s some variation. This test runs over 100K words, and it still only takes a few milliseconds either way.

The results are similar to those in my previous post, which described a similar performance test using Node.js. This comes as no surprise, because Node.js runs on V8.

On the first run, it looks like a for loop performs a bit better than the some method. There’s some optimization on the second run. This is all déjà vu, if you read my last post.

Is it worth using a for loop everywhere to take advantage of that optimization? I think you’d really want to go deeper in your performance tests to understand the optimization, and make sure that it will be used before deciding to go with for loops everywhere. You will also want to be sure that it’s worth sacrificing code maintainability and readability in pursuit of some possibly minuscule performance boost.

It’s important to be aware of the potential for any performance impacts of special Array methods when architecting a new project or looking for bottlenecks in an existing one. But deciding never to use Array iterators just because you heard a vague rumor that they were not performant is unwise.

A performance test of the JavaScript Array.some function in Node.js

In my previous blog post, I talked about using the Array.some method as a handy tool for looping over “some” array elements until a condition is satisfied. Well, once in a while, you’re going to come across an article which is critical of “new-fangled” Array methods. It’s a known issue that some of these array methods perform poorly in comparison to the “old-fashioned” for loop.

It’s not that people don’t like the new methods. In the article mentioned above, Yotam Kadishay says they make JavaScript “more functional”, and writing code is “more fun and smooth” with the addition of these new methods. But if your code runs like a dog, no one is going to really care about how much fun you had writing it, will they?

Mr. Kadishay wrote a good article, and you should read it! He performance tested some of the newer Array methods, and found they tend to run slower than the older, tried and true ones. But he didn’t discuss the performance of the Array.some method. Array.some is actually not so new-fangled – it has been around since JavaScript 1.6 (2006).

So I’m going to performance test the implementation of Array.some, which is what I used in implementing my startsWithVowel method I wrote in my previous post. For test purposes, I’ve snagged Peter Norvig’s list of 100,000 words – they are all uppercase. I saved this list to my /tmp directory.

To be fair, I might want to run my performance test with a list of lowercased words, a mix of cases and so on. But for now let’s just work with this list.

I’m going to test this in Node.js. I use nvm (Node Version Manager) to use whichever version of node I’m interested in testing. I’ll test using node 8.16. I have node installed on a development environment running Ubuntu 16.04. To run my test, I fire up a node command line as follows:

nvm use 8.16
node
>

Then I just copy and paste the code below into the command line and look at the results:

/* Displays results */
var finishAndPrint = function(words) {
    console.log("words length: " + words.length);
    console.log("First word: " + words[0]);
    console.log("Last word: " + words[words.length-1]);
};

/* This test function is guaranteed to never match a word in the Norvig list of words */
var testFunction = function(item) {
    return item === "gsdjkoeu";
};

/* A performance test using the Array.some method: */
var performanceTestSome = function(words) {
    const {performance} = require('perf_hooks');
    let t0 = performance.now();
    let result = words.some(function(el, i) {
        return testFunction(el);
    });
    let t1 = performance.now();
    console.log("The some method took " + (t1 - t0) + " milliseconds and the result was " + result + ".");
};

/* A performance test using a simple for loop: */
var performanceTestForLoop = function(words) {
    const {performance} = require('perf_hooks');
    let len = words.length;
    var result = false;
    let t0 = performance.now();
    for (var i = 0; i < len; i++) {
        if (testFunction(words[i])) {
            // This code is necessary so the function does the same thing as the "some" method
            result = true;
            break;
        }
    }
    let t1 = performance.now();
    console.log("The for loop method took " + (t1 - t0) + " milliseconds and the result was " + result + ".");
};

/* Read the words: */
fs.readFile('/tmp/count_1w100k.txt', {encoding: 'utf-8'}, function(err, data) {
    var words = [];
    var lines = data.split('\n');
    lines.forEach(function(row, i) {
        var items = row.split('\t');
        if (items[0].length > 0) {
            words.push(items[0]);
        }
    });
    finishAndPrint(words);
    performanceTestSome(words);
    performanceTestForLoop(words);
    performanceTestForLoop(words);
    performanceTestSome(words);
});

Here’s the output:

> words length: 100000
> First word: THE
> Last word: PGY
> The some method took 2.5111299753189087 milliseconds and the result was false.
> The for loop method took 1.983236014842987 milliseconds and the result was false.
> The for loop method took 0.6664620041847229 milliseconds and the result was false.
> The some method took 1.9726130068302155 milliseconds and the result was false.

Huh, there’s some kind of optimization going on in that for loop. I didn’t investigate. I’m assuming the first run is more accurate. It looks like the for loop is slightly more performant than the some method.

I repeated the test in a couple of different ways, because I wanted to make sure that the order of calling didn’t matter. In each case, I exited the node command line using .exit and restarted it. Here’s another example output, in which I called the for loop method twice followed by the some test. In this case, it looks as if the performance difference is negligible on the first run of each method. However, it continues to appear that there’s some optimization which is done when the for loop method is used the second time, but not when some is used.

> words length: 100000
> First word: THE
> Last word: PGY
> The for loop method took 2.146267980337143 milliseconds and the result was false.
> The for loop method took 0.590969979763031 milliseconds and the result was false.
> The some method took 2.074671983718872 milliseconds and the result was false.
> The some method took 1.9943189918994904 milliseconds and the result was false.

Since I’ve got my code here and explained the methods I used, together with the environment, you can repeat the tests for yourself. I think it’s important to give as much information about your methods and environment when running performance tests. And if I were going to get more serious, I’d probably do some statistics, maybe run this test multiple times, and do a chi-squared test or some sort of test to make sure that my results are not a fluke.

Before you consider performance testing, think about whether performance will ever truly be an issue. Is it worth performance testing when you’re building a prototype for a startup and the code may never even get used by more than a few people? Is it worth sacrificing readability and maintainability in order to run the most performant code? Robert Harvey wrote this comment in response to a Stack Overflow question about performance testing Node.js:

you can have slow code run forever where a user won’t notice it, and pretty fast code running where they do, and they will complain about the pretty fast code not being fast enough. Or that your request to your server API took 220ms. Or something else like that. The point remains that if you take a profiler out and go looking for work to do, you will find it, but it may not be the work your users need.

It is probably true that performance is more important when writing server-side code. Your server’s going to be fielding a bunch of requests from multiple users; whereas the browser client is just dealing with “one user” at a time. Of course, you want good performance on the client side as well. You may want to play it safe and use for loops everywhere. That may be overkill 🙂 You’d still better know how Array methods work, and know how to read them, because there are plenty of developers and companies which are not nearly that strict about it.

In my next blog post, I’ll run the same test using JavaScript on the client.