# How to set the function to wait until libraries loaded

**URL:** <https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373>\
**Category:** General\
**Created:** [8 November 2021 12:25 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373 "2021-11-08T12:25:34Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![nerevar](https://avatars.discourse-cdn.com/v4/letter/n/f475e1/32.png) [@nerevar](https://discourse.nodered.org/u/nerevar)\
**Post date:** [8 November 2021 12:25 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/1 "2021-11-08T12:25:34Z")

</div>

Hello,

I am using vega charts on my dashboard and certain users are on Wi-Fi and sometimes charts can't load in time, so they get a blank widget.

This is the code without timeout:

```auto
<html>
    <head>
        <meta charset="utf-8">
        <script src="http://localhost:1880/vega.js"></script>
        <script src="http://localhost:1880/vega-embed.js"></script>
    </head>
    <body>  
        <div id="vis" style="height:500px"></div>
        <script>
            (function(scope) {
                scope.$watch('msg.vega', function(data) {
                    const spec = data;
                    vegaEmbed("#vis", spec).then(result => console.log(result)).catch(console.warn);

                });
            })(scope);
        </script>
    </body>
</html>

```

I used to have a timeout:

```auto
                    setTimeout(()=> {

                    }, 500)

```

But sometimes it is not enough - and I wouldn't like to increase this time because then the charts would load too slow.

How to make it so the vega charts are sent to the dashboard only when all the libraries are loaded?

---

<div class="post-metadata">

**Author:** ![UnborN](https://avatars.discourse-cdn.com/v4/letter/u/4491bb/32.png) [@UnborN](https://discourse.nodered.org/u/UnborN)\
**Post date:** [9 November 2021 14:35 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/2 "2021-11-09T14:35:25Z")

</div>

Hello Tomislav,

> [@nerevar](#):
>
> sometimes charts can't load in time

I was wondering about this also when i was testing the code in your thread for the Apexcharts

you can try creating a seperate `ui_template` and set its Template type : to add to head section

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/2/3/230efe757204503bde364e0b4537031bd386e0ab.png)

and move in this new node just the script tags of your libaries  
hopefully this way they should load first before the rest of the code in the main ui\_template

---

<div class="post-metadata">

**Author:** ![nerevar](https://avatars.discourse-cdn.com/v4/letter/n/f475e1/32.png) [@nerevar](https://discourse.nodered.org/u/nerevar)\
**Post date:** [10 November 2021 08:09 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/3 "2021-11-10T08:09:57Z")

</div>

Upon further testing - I found out that it doesn't load every time (I've used the CDN online libraries to simulate the lag).

So, we need a javascript solution to the problem ☹

---

<div class="post-metadata">

**Author:** ![nerevar](https://avatars.discourse-cdn.com/v4/letter/n/f475e1/32.png) [@nerevar](https://discourse.nodered.org/u/nerevar)\
**Post date:** [10 November 2021 09:26 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/4 "2021-11-10T09:26:52Z")

</div>

OK, this libraries are placed in the head section:

```auto
<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@5"></script>

```

This means they are only loaded once?  
So If I don't clear cache in my browser, node-red shouldn't load them again?

Next,

The code for the widget stays the same:

```auto
<html>
    <body>  
        <div id="vis" style="height:500px"></div>
        <script>
            (function(scope) {
                scope.$watch('msg.vega', function(data) {
                    const spec = data;
                    vegaEmbed("#vis", spec).then(result => console.log(result)).catch(console.warn);

                });
            })(scope);
        </script>
    </body>
</html>

```

Next, I have found many topics regarding this, but I'm not sure how to check the scripts are loaded, and pause the execution of the vegaEmbed..

jQuerry:

> <https://stackoverflow.com/questions/9521298/verify-external-script-is-loaded>

My scripts do always load, but sometimes I need only the time pause..

---

<div class="post-metadata">

**Author:** ![UnborN](https://avatars.discourse-cdn.com/v4/letter/u/4491bb/32.png) [@UnborN](https://discourse.nodered.org/u/UnborN)\
**Post date:** [10 November 2021 13:25 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/5 "2021-11-10T13:25:00Z")

</div>

> [@nerevar](#):
>
> I have found many topics regarding this, but I'm not sure how to check the scripts are loaded

i didnt find a simple way of doing this either .. i studied your link but its beyond my programming skills .  
i found one example of script loading on the MDN website

```auto
[{"id":"f6b104981899da0e","type":"ui_template","z":"5847b7aa62131d37","group":"cecf42695640eca4","name":"","order":0,"width":0,"height":0,"format":"<div id=\"vis\" style=\"height:500px\"></div>\n<script>\n\nlet nr = scope;\n\nfunction loadError(oError) {\n throw new URIError(\"The script \" + oError.target.src + \" didn't load correctly.\");\n}\n\nfunction affixScriptToHead(url, onloadFunction) {\n var newScript = document.createElement(\"script\");\n newScript.onerror = loadError;\n if (onloadFunction) { newScript.onload = onloadFunction; }\n document.head.appendChild(newScript);\n newScript.src = url;\n}\n\n// main code runs with this function\nfunction myJS() {\nnr.$watch('msg.vega', function(data) {\n const spec = data;\n console.log(\"Mesg received from NR\", spec)\n \n vegaEmbed(\"#vis\", spec).then(result => console.log(result)).catch(console.warn);\n\n});\n}\n\naffixScriptToHead(\"https://cdn.jsdelivr.net/npm/vega@5\");\naffixScriptToHead(\"https://cdn.jsdelivr.net/npm/vega-lite@5\");\naffixScriptToHead(\"https://cdn.jsdelivr.net/npm/vega-embed@6\", () => { \n console.log(\"The scripts has been correctly loaded.\");\n nr.send({topic: \"FROM UI\", payload : \"The scripts has been correctly loaded.\"})\n // console.log(this);\n //console.log(nr);\n console.log(vegaEmbed)\n myJS() // run main function after scripts\n })\n\n\n\n</script>\n\n\n\n\n","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"local","className":"","x":750,"y":640,"wires":[["b7e50c3d856e85c5"]]},{"id":"b7e50c3d856e85c5","type":"debug","z":"5847b7aa62131d37","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":960,"y":640,"wires":[]},{"id":"66d1189f909dbae5","type":"inject","z":"5847b7aa62131d37","name":"","props":[{"p":"payload"},{"p":"vega","v":"[1,2,3,4]","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":540,"y":640,"wires":[["f6b104981899da0e"]]},{"id":"cecf42695640eca4","type":"ui_group","name":"Default","tab":"39a6d442788cfb84","order":1,"disp":true,"width":"24","collapse":false,"className":""},{"id":"39a6d442788cfb84","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}]

```

---

<div class="post-metadata">

**Author:** ![UnborN](https://avatars.discourse-cdn.com/v4/letter/u/4491bb/32.png) [@UnborN](https://discourse.nodered.org/u/UnborN)\
**Post date:** [10 November 2021 13:49 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/6 "2021-11-10T13:49:18Z")

</div>

the above doesnt look very clean ..  
maybe a simple setInterval that clears when the Vega library is loaded ?

```auto
<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>

<div id="vis" style="height:500px"></div>
<script>

let scriptCheck = setInterval( () => {
console.log("Checking Scripts")
if (vegaEmbed) {
  console.log("Vega Scripts Loaded")
  clearInterval(scriptCheck);
  }

}, 200)

</script>

```

---

<div class="post-metadata">

**Author:** ![Buckskin](https://avatars.discourse-cdn.com/v4/letter/b/b9e5f3/32.png) [@Buckskin](https://discourse.nodered.org/u/Buckskin)\
**Post date:** [10 November 2021 20:30 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/7 "2021-11-10T20:30:32Z")

</div>

I have no idea if this will help but have you tried the JQuery document ready ` $(function() {`

```auto
<script>
    (function(scope) {
      // Important. Use JQuery document ready function so that the Unique id is created & loaded
      $(function() {

```

---

<div class="post-metadata">

**Author:** ![UnborN](https://avatars.discourse-cdn.com/v4/letter/u/4491bb/32.png) [@UnborN](https://discourse.nodered.org/u/UnborN)\
**Post date:** [10 November 2021 22:47 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/8 "2021-11-10T22:47:48Z")

</div>

@Buckskin i didnt manage to make it work .. do you have a sample code ?

I had another go with `getScript` .. seems it can work .. but with those callbacks its getting uglier with every call 😅

```auto

<script>

let theScope = scope;
 
$.getScript("https://cdn.jsdelivr.net/npm/vega@5", function (data, textStatus, jqxhr) {
  console.log("Script1", textStatus, jqxhr.status, "Load was performed."); // Success

  $.getScript("https://cdn.jsdelivr.net/npm/vega-lite@5", function (data, textStatus, jqxhr) {
    console.log("Script2", textStatus, jqxhr.status, "Load was performed."); // Success

    $.getScript("https://cdn.jsdelivr.net/npm/vega-embed@6", function (data, textStatus, jqxhr) {
      console.log("Script3", textStatus, jqxhr.status, "Load was performed."); // Success
      
      main() // run main

    });
  });
});

// Main code goes here //
let main = () => {
    console.log( "ready!" );
    theScope.send( { payload: "ready!" } ); // send msg to NR
    console.log( vegaEmbed );
};

</script>

```

---

<div class="post-metadata">

**Author:** ![Buckskin](https://avatars.discourse-cdn.com/v4/letter/b/b9e5f3/32.png) [@Buckskin](https://discourse.nodered.org/u/Buckskin)\
**Post date:** [10 November 2021 23:34 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/9 "2021-11-10T23:34:52Z")

</div>

Try something like this:

```auto
<head>
        <script defer="true" src="https://cdn.jsdelivr.net/npm/vega@5">></script>
        <script defer="true" src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
        <script defer="true" src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
</head>

<body>
        
        <div id="vis" style="height:500px">{{load}}</div>
    
    <script>
        (function(scope) {
            // Important. Use JQuery document ready function so that the Unique id is created & loaded
                $(function() {

                    scope.load = 'I am not here yet'

                    if (vegaEmbed('#vis')) {
                        console.log("Vega Scripts Loaded")

                        scope.load = 'Here I am'

                    }

                })

        })(scope)

    </script>

</body>

```

I get `'Here I am'` in the Group space on screen but without any data I cannot tell if it is actually working

---

<div class="post-metadata">

**Author:** ![nerevar](https://avatars.discourse-cdn.com/v4/letter/n/f475e1/32.png) [@nerevar](https://discourse.nodered.org/u/nerevar)\
**Post date:** [11 November 2021 13:36 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/10 "2021-11-11T13:36:54Z")

</div>

> [@UnborN](#):
>
> the above doesnt look very clean ..  
> maybe a simple setInterval that clears when the Vega library is loaded ?

I can't seem to stop the setInterval function - it runs indefinately..

> [@Buckskin](#):
>
> I get `'Here I am'` in the Group space on screen but without any data I cannot tell if it is actually working

Sometimes it loads, sometimes it doesn't - Its like before, and I get some errors:

```auto
VM1823:1 Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0
    at JSON.parse (<anonymous>)
    at vega-embed@5:1
    at Generator.next (<anonymous>)
    at a (vega-embed@5:1)

```

> [@UnborN](#):
>
> I had another go with `getScript` .. seems it can work .. but with those callbacks its getting uglier with every call 😅

Thank you! It seems it works - I'll test it on the production Pi right now.  
There is only 1 yellow error, but I think this is vega related:

```auto
TypeError: Cannot read properties of undefined (reading 'usermeta')
    at vega-embed@5?_=1636637716662:1
    at Generator.next (<anonymous>)
    at vega-embed@5?_=1636637716662:1
    at new Promise (<anonymous>)
    at ce (vega-embed@5?_=1636637716662:1)
    at at (vega-embed@5?_=1636637716662:1)
    at lt (vega-embed@5?_=1636637716662:1)
    at <anonymous>:28:21
    at m.$digest (app.min.js:174)
    at m.$apply (app.min.js:177)

```

---

<div class="post-metadata">

**Author:** ![nerevar](https://avatars.discourse-cdn.com/v4/letter/n/f475e1/32.png) [@nerevar](https://discourse.nodered.org/u/nerevar)\
**Post date:** [11 November 2021 14:02 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/11 "2021-11-11T14:02:45Z")

</div>

> [@UnborN](#):
>
> I had another go with `getScript` .. seems it can work .. but with those callbacks its getting uglier with every call 😅

```auto
let main = () => {
    console.log( "ready!" );
    theScope.send( { payload: "ready!" } ); // send msg to NR
    console.log( vegaEmbed );
    theScope.$watch('msg.vega', function(data) {
        const spec = data;
        vegaEmbed("#vis", spec, {"actions": {export: true, source: false, editor: false}}).then(result => console.log(result)).catch(console.warn);
    }); 
};

```

On live Pi, this code does run charts load correctly the first time.  
First problem is It does not get my refresh message, so **it is blank until new message appears**.  
The original does load with my refresh message:

```auto
        <meta charset="utf-8">
        <script src="http://192.168.x.xxx:1880/vega.js"></script>
        <script src="http://192.168.x.xxx:1880/vega-embed.js"></script>
        <div id="vis" style="height:500px"></div>
        <script>
            (function(scope) {
                scope.$watch('msg.vega', function(data) {
                    const spec = data;
                    setTimeout(()=> {
                      	vegaEmbed("#vis", spec, {"actions": {export: true, source: false, editor: false}})
                          .then(result => console.log(result))
                          .catch(console.warn);
                    }, 500) 
           
                });
            })(scope);
        </script>

```

The bigger problem is that **every next load (cached) it throws this error**:

```auto
VM2987:1 Uncaught SyntaxError: Identifier 'theScope' has already been declared
    at b (app.min.js:20)
    at Ie (app.min.js:20)
    at k.fn.init.append (app.min.js:20)
    at k.fn.init.<anonymous> (app.min.js:20)
    at _ (app.min.js:20)
    at k.fn.init.html (app.min.js:20)
    at app.min.js:596
    at m.$digest (app.min.js:172)
    at m.$apply (app.min.js:176)
    at app.min.js:593

```

And stops any further chart loads until I clear the cache from this page.

---

<div class="post-metadata">

**Author:** ![UnborN](https://avatars.discourse-cdn.com/v4/letter/u/4491bb/32.png) [@UnborN](https://discourse.nodered.org/u/UnborN)\
**Post date:** [11 November 2021 14:38 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/12 "2021-11-11T14:38:47Z")

</div>

We dont have your chart data or your flow so we cannot reproduce it.

> [@nerevar](#):
>
> `Uncaught SyntaxError: Identifier 'theScope' has already been declared`

yes, but its just a warning because this line ⬇ sends a msg regardless. so i dont think it blocks the rest of the code.

```auto
theScope.send( { payload: "ready!" } ); // send msg to NR

```

I give up .. its something with the inner workings of AngularJS injecting html, scripts etc that need 5 years experience in angular to figure it out .. hint hint ..

Use **ui\_builder** if you dont already have anything else on the Dashboard

> **[node-red-contrib-uibuilder](https://flows.nodered.org/node/node-red-contrib-uibuilder)**
>
> Easily create data-driven web UI's for Node-RED using any (or no) front-end library.

---

<div class="post-metadata">

**Author:** ![nerevar](https://avatars.discourse-cdn.com/v4/letter/n/f475e1/32.png) [@nerevar](https://discourse.nodered.org/u/nerevar)\
**Post date:** [12 November 2021 06:54 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/13 "2021-11-12T06:54:04Z")

</div>

I think your code is the solution, but unfortunately I am at a loss with javascript (just realized I was working with plain NodeJS and html most of the time - I would like to learn through projects, though).

What I was about to say is: the code throws an error and stops when tab switching.  
Try this tab switching flow: [flows (64).json](https://discourse.nodered.org/uploads/short-url/5S1IclZXMlmRLY5iqcQgrO9MR3k.json) (2.4 KB)

Regarding my dashboard - It's a production monitoring mdashboard created in 06/2020 and used for monitoring the total of 43 signals from 9 machines. Downloaded flow size is 2,9Mb - so switch to uibuilder should be version 2.0 (project of its own).

Thank you in advance

---

<div class="post-metadata">

**Author:** ![nerevar](https://avatars.discourse-cdn.com/v4/letter/n/f475e1/32.png) [@nerevar](https://discourse.nodered.org/u/nerevar)\
**Post date:** [12 November 2021 07:43 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/14 "2021-11-12T07:43:44Z")

</div>

> [@UnborN](#):
>
> ```auto
> <script>
> **var** theScope = scope;
> $.getScript("https://cdn.jsdelivr.net/npm/vega@5", function (data, textStatus, jqxhr) {
> console.log("Script1", textStatus, jqxhr.status, "Load was performed."); // Success
> $.getScript("https://cdn.jsdelivr.net/npm/vega-embed@5", function (data, textStatus, jqxhr) {
> console.log("Script3", textStatus, jqxhr.status, "Load was performed."); // Success
>       
> main() // run main
> });
> });
> 
> // Main code goes here //
> **let** main = () => {
> console.log( "ready!" );
> theScope.send( { payload: "ready!" } ); // send msg to NR
> console.log( vegaEmbed );
> };
> </script>
> 
> ```

Sorry I was being lazy - the solution was to declare theScope with var (because let is a block scoped), and to remove declaration of the main callback.

Now, only to get the chart to load on my refresh....

---

<div class="post-metadata">

**Author:** ![nerevar](https://avatars.discourse-cdn.com/v4/letter/n/f475e1/32.png) [@nerevar](https://discourse.nodered.org/u/nerevar)\
**Post date:** [12 November 2021 07:54 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/15 "2021-11-12T07:54:38Z")

</div>

Ok I've got it. Thanks all.

```auto
if (msg.payload === "ready!") {
    msg = {};
    msg.topic = "refresh";
    return msg;
}

```

---

<div class="post-metadata">

**Author:** ![UnborN](https://avatars.discourse-cdn.com/v4/letter/u/4491bb/32.png) [@UnborN](https://discourse.nodered.org/u/UnborN)\
**Post date:** [13 November 2021 00:33 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/16 "2021-11-13T00:33:02Z")

</div>

Hello Tomislav,

nice find regarding that difference between `let` and `var` 👍

i was reading on Dashboard github and i found a post with a person asking similar question ..  
he also went with the `setInterval` approach to check whether the scripts were loaded.

Here it is if you want to read

### [Inject js in page](https://github.com/node-red/node-red-dashboard/issues/370)

---

<div class="post-metadata">

**Author:** ![UnborN](https://avatars.discourse-cdn.com/v4/letter/u/4491bb/32.png) [@UnborN](https://discourse.nodered.org/u/UnborN)\
**Post date:** [13 November 2021 20:44 UTC](https://discourse.nodered.org/t/how-to-set-the-function-to-wait-until-libraries-loaded/53373/17 "2021-11-13T20:44:09Z")

</div>

Here's another ugly example using an array of Promises that waits for the loading of the scripts to resolve

```auto
[{"id":"d52180de48148c7f","type":"ui_template","z":"5847b7aa62131d37","group":"62350f50d4b09ff8","name":"Script Loading - Promises","order":21,"width":"2","height":1,"format":"<script>\n (function (scope) {\n\n var scope = scope\n\n function loadScript(url) {\n return new Promise(function (resolve, reject) {\n let script = document.createElement('script');\n script.src = url;\n script.async = false;\n script.onload = function () {\n resolve(url);\n };\n script.onerror = function () {\n reject(url);\n };\n document.body.appendChild(script);\n });\n }\n\n let scripts = [\n 'https://cdn.jsdelivr.net/npm/vega@5',\n 'https://cdn.jsdelivr.net/npm/vega-lite@5',\n 'https://cdn.jsdelivr.net/npm/vega-embed@6'\n];\n\n // save all Promises as array\n let promises = [];\n scripts.forEach(function (url) {\n promises.push(loadScript(url));\n });\n\n Promise.all(promises)\n .then(function () {\n console.log('all scripts loaded');\n //console.log('scope', scope);\n scope.$watch('msg', function (msg) {\n if (msg) scope.send({ topic: \"Vega loaded and received msg from NR !!!\", payload: msg.payload })\n });\n\n }).catch(function (script) { console.log(script + ' failed to load'); });\n\n\n })(scope)\n\n</script>","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"local","className":"","x":670,"y":620,"wires":[["d1e9cc937a2a6269"]]},{"id":"d1e9cc937a2a6269","type":"debug","z":"5847b7aa62131d37","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":870,"y":620,"wires":[]},{"id":"ba8d624f24041fd3","type":"inject","z":"5847b7aa62131d37","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":440,"y":620,"wires":[["d52180de48148c7f"]]},{"id":"62350f50d4b09ff8","type":"ui_group","name":"Promises","tab":"554e77d51ad13f2d","order":1,"disp":false,"width":"24","collapse":false,"className":""},{"id":"554e77d51ad13f2d","type":"ui_tab","name":"","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

```
