Tuesday, December 3, 2019

Update styles in bulk via JavaScript

Updating an element style using javascript is pretty easy: document.getElementById(id).style.property = new style. But what if we have hundreds/thousands/.. elements to be updated? Calling the property setter above will force page re-rendering every time, so it would take considerable time. That's where bulk update is really helpful. Instead of applying styles after every change we can compose the css sheet with all the styles collected together and appy it at once.

Tuesday, October 29, 2019

Array.prototype.forEach() function does not await..

Recently I discovered that Array.prototype.forEach() function is not suitable for 'awaitable' functions. Let's have a look at an example.

const getAsyncData = (n) => {
   return new Promise((resolve, reject) => {
      setTimeout(() => resolve(n), 500)
   })
};

const arr = [1,2,3,4,5];

//forEach example
(async () => {
   let total = 0;
   arr.forEach(async n => {
      let res = await getAsyncData(n);
      console.log(res);
      total += res;
   });
  
   console.log('total = ' + total);
})();

I expected that forEach will await for every call of the getAsyncData() function so that to calculate total correctly, but surprisingly the result was different.

total = 0
1
2
3
4
5

Obviously, forEach ignores await keyword and quits immediately so total is not calculated.

Sunday, September 15, 2019

React create-react-app edit WebPack DevServer settings (adding SSL certificate options)

I have a React application that has been created with the create-react-app tool and I needed to change WebPack DevServer settings. Three the most popular suggestions I found on the internet were:
  • npm run eject
  • fork the react-scripts and make necessary changes
  • use a library like react-app-rewired
Unfortunately, all of them have various drawbacks (I will not discuss them here) and require too much effort. So finally, I found the simplest possible solution. The config file is here
'./node_modules/react-scripts/config/webpackDevServer.config.js'. Just edit the file here and that's it! For example, my task was to add an SSL certificate (.pfx). So for that I had to do 3 simple steps:
  1. Create a new folder security on the root app level and put the certificate file into it.
  2. Add the string HTTPS=true to the .env file. If you don't have the file, just create it on the root app level.
  3. Add the two options to the config file
Well, if you have another password for your certificate, update the pfxPassphrase field. If you have a different certificate type, the fields must be different. Reference to the official WebPack DevServer documentation, there is the entire option list https://webpack.js.org/configuration/dev-server/

But this solution has one drawback - the changes above are not saved when we delete node_modules folder and install everything from scratch with npm install.
I have found the simple fix for that - to make a script that overrides the config file when necessary.
  1. Create a new folder config on the root app level and copy the updated webpackDevServer.config.js file into it.
  2. Create a new script postInstall.js on the root app level and insert the following code inside it:
    const fs = require('fs');
    
    fs.copyFile(
        './config/webpackDevServer.config.js',
        './node_modules/react-scripts/config/webpackDevServer.config.js',
        err => {
            if (err) {
                console.error('Cannot copy webpackDevServer.config');
                console.error(err);
                throw err;
            }
    
            console.log('webpackDevServer.config is copied');
        }
    );
    
  3. Add the following line into the package.json file, scripts section: "post-install": "node ./postInstall.js"

So now, after npm install just run another command npm run post-install and that's it - your webpackDevServer.config file has been copied from the /config folder to the destination. Have a nice day!

Monday, July 8, 2019

jQuery autocomplete table

Jquery allows us to build really beautiful autocomplete box. For example, my task recently was to create a table-view autocomplete. That's what I made:



You can try it on JSFiddler:
https://jsfiddle.net/AndrewBuntsev/8jbqyrv4/

Thursday, June 20, 2019

Array.prototype.map(parseInt) tricky question

When I ran the following JS
['1', '7', '11'].map(parseInt);
I surprisingly saw the result [1, NaN, 3] instead of [1, 7, 11]. But if we try parseFloat
['1', '7', '11'].map(parseFloat);
the result is correct [1, 7, 11]
If we try the extended form of parseInt
['1', '7', '11'].map(i => parseInt(i));
we get the proper outcome as well.

So why is it so messy with the short parseInt?

Sunday, May 26, 2019

JavaScript string memory leak

If you create the following HTML file
<html>
  <body>
    <script>
      function createString() {
        return "0".repeat(25 * 1024 * 1024).substring(0, 12);
      }

      var arr = [];

      setInterval(function() {
         let str = createString();
         arr.push(str);
      }, 500);
    </script>
  </body>
</html>
run it in your browser and open the task manager you could notice that the browser takes some limited amount of memory ~200Mb. But what if we update one single digit in our code
return "0".repeat(25 * 1024 * 1024).substring(0, 13);
i.e. replace 12 with 13, run it into a browser and have a look at the process memory. It grows unstoppable and makes the browser crash in a half a minute! Why does it happen?

Every time we call String.prototype.substring function it returns new string instance that keeps .. a link to the original string! That's how V8 organizes memory for strings for permamance reason. Exception is when a new instance string length is less than 13 - in that case no link to original string preserved. So in our example arr contains no just short strings(13), but actually the huge ones (25 * 1024 * 1024). How to avoid it? Unfortunatelly, there is no documemnted method to 'cut' parents from strings, that's because we can do the following (update line 11):
<html>
  <body>
    <script>
      function createString() {
        return "0".repeat(25 * 1024 * 1024).substring(0, 12);
      }

      var arr = [];

      setInterval(function() {
         let str = createString().split('').join('');
         arr.push(str);
      }, 500);
    </script>
  </body>
</html>
That manoeuvre breaks the link with the original string and prevents memory leak.

Wednesday, May 15, 2019

Install NodeJS onto Ubuntu

The task sounds quite simple, but after I failed to do it several times I discovered that would be good to make a post about it. Just to have a step-by-step guide to save time when I decide to do it next time.

Firstly, let's remove old Node leftovers from previous installations
~$ sudo apt remove node
~$ sudo apt remove nodejs

and remove the node folder as well
~$ rm -rf node_modules/

That's time to download and install it in appropriate way.