I want to verify the user credentials before any page of my site load, so I put an script in the top of each of my HTML document. But this script in particularly has Async functions (because it sends API calls for the verification), and I noticed that other elements load before this script when some Async function has to be done (inside of this particularly script), even I putting at the top of the HTML.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap" rel="stylesheet">
<link href="home.css" rel="stylesheet">
<script src="../isUserLogged.js"></script> <!--This is the script that I mentioned-->
<script src="home.js" defer></script> <!--This script is running before even with "DEFER" annotation-->
<title>Caderneta</title>
</head>
<body>
<div class="header">
<p id="home-txt" class="header-txt">Home</p>
<p id="user-txt" class="header-txt">Olá, Nome!</p>
</div>
<div class="content">
<iframe src="../notebooks/notebooks.html"></iframe>
</div>
</body>
</html>
"isUserLogged.js":
//Local Storage
const token = localStorage.getItem("token");
async function isUserSessionValid() {
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": token
},
body: token
};
try {
const response = await fetch("http://localhost:8080/teachers/get-by-token", options);
if(response.ok) {
sessionStorage.setItem("user", JSON.stringify(await response.json()));
return true;
}
} catch(e) {
}
alert("Erro ao validar sessão, faça login ou tente novamente mais tarde.");
return false;
}
async function validateUserSession() {
if(token != null) {
if(await isUserSessionValid()) {
return;
}
}
localStorage.clear();
sessionStorage.clear();
location.replace("../login/login.html");
}
validateUserSession();
What can I do to the elements just load after all async functions has fineshed?
2
Answers
According to the documentation the script with the
defer
attribute will be executed after parsing the document but NOT after rendering the document. The reason, why you feel like the script executed before is a Rendering Delay.As
@Phil
mentioned in the comment, You have to do some workarounds like Adding a loading screen and hiding it when your function execution is completed.You are trying to delay HTML parsing using an async function, that won’t work.
First you need to understand what
defer
is ‘deferring’ wrt. Defer is akin to putting a regular script right at the bottom of your page. This means, deferred scripts will ALWAYS fire right beforeDOMContentLoaded
event of your window. Any promises or async scripts inside prior regular script will not stop parsing HTML further.DOMContentLoaded
means you can fully see the initial HTML text with placeholders for images etc., without guarantee ofimg
s,video
s, dynamicstylesheet
s,link
s andasync
scripts. Once all of these are loaded, a separateload
event fires. So,load
comes afterDOMContentLoaded
.Async scripts that come with the page give you 0 guarantee. They must fire before
load
but some can also fire beforeDOMContentLoaded
. Theasync
attribute essentially offloads downloading of these assets to a separate thread.At this point you have 4 choices:
isUserLogged.js
and the server responds with aContent-type
:text/html
resembling a loggin page.validateUserSession
to hide whathome.js
operates on.home.js
in a function and execute withinvalidateUserSession.then(...)
If you want to go with 4th choice, my solution would be to do something like this:
in your html:
Pay attention that both scripts now
async
and there is an additional regulartaskq
script at the top.Now with a bit of change you can have the effect you want, essentially wrap your existing functions with a iife (immediately invoked function expression) and add a few props.
I put everything inside
isUserLogged
to a function calledlogMe
and wrapped it within an iife. I gave it a_taskqId
of first and pushed it into execution flow. I paused/resumed taskq within validate function. I additionally exported a variable which you can use down the line.Essentially the same for
home.js
:Above function will wait for
first
to execute. ThetokenObj
we exported earlier is also available insidehome
, sotokenObj.value
is your user’stoken
. And that’s it.You could also have used taskq.load("./home.js") inside
isUserLogged.js
instead oftaskq.pause/taskq.resume
, you have several options.Note that this solution will work on ie10 included and can easily be combined with modern es6 modules as well. You can always roll your own solution similar to taskq and they all revolve around storing functions prior to execution.
Disclaimer: I am the developer of taskq.