在任何元素之前加载带有异步函数的脚本

问题描述 投票:0回答:2

我想在加载网站的任何页面之前验证用户凭据,因此我在每个 HTML 文档的顶部放置了一个脚本。但是这个脚本特别具有异步函数(因为它发送 API 调用进行验证),并且我注意到,当必须执行某些异步函数时(在这个特定脚本内部),其他元素会在此脚本之前加载,即使我将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();

在所有异步函数完成后,我可以对刚刚加载的元素做什么?

javascript html dom async.js
2个回答
0
投票

根据文档,具有

defer
属性的脚本将在解析文档之后执行,但不是渲染文档之后执行。原因,为什么你感觉之前执行的脚本是渲染延迟

正如评论中提到的

@Phil

,您必须采取一些解决方法,例如添加加载屏幕并在函数执行完成时隐藏它

<body> <div id='loader' class='loader'>Loading...</div> <!-- Rest of your code --> </body>
.loader {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
async function validateUserSession() {
    if(token != null) {
        if(await isUserSessionValid()) {
            const loader = document.getElementById('loader');
            loader.style.display = 'none';
            return;
        }
    }
    localStorage.clear();
    sessionStorage.clear();
    location.replace("../login/login.html");
}
    

0
投票
您尝试使用异步函数延迟 HTML 解析,但这是行不通的。

首先你需要了解什么是“推迟”。 Defer 类似于将常规脚本放在页面底部。这意味着,延迟脚本将始终在窗口的

defer

 事件之前触发。之前的常规脚本中的任何 Promise 或异步脚本都不会停止进一步解析 HTML。
DOMContentLoaded

意味着您可以完全看到带有图像等占位符的初始 HTML 文本,而不保证

DOMContentLoaded

img
、动态 
video
stylesheet
link
 脚本。一旦所有这些都加载完毕,就会触发一个单独的 
async
 事件。所以,
load
load
 之后。
页面自带的异步脚本给你0保证。它们必须在 
DOMContentLoaded

之前开火,但有些也可以在

load

 之前开火。 
DOMContentLoaded
 属性本质上将这些资产的下载卸载到单独的线程。
此时你有3个选择:

制作一个专门的 HTML 页面,仅运行

async
    ,服务器会以
  1. isUserLogged.js
     响应:
    Content-type
    ,类似于登录页面。
    使用 css 以可视方式隐藏元素,直到异步函数返回 true 或登录详细信息。
  2. 使所有脚本异步并强制执行顺序。
  3. 如果您想选择第三种选择,我的解决方案是执行以下操作:
在你的html中:

text/html

请注意,现在两个脚本都
<head>
    <script src="https://cdn.jsdelivr.net/npm/@ibowankenobi/taskq/taskq.js"></script>
    ...
    <script src="../isUserLogged.js" async></script> 
    <script src="home.js" async></script> 
</head>
,并且顶部还有一个额外的

常规

async脚本。
现在只需进行一些更改即可获得所需的效果,本质上是用 iife(立即调用函数表达式)包装现有函数并添加一些 props。

taskq

我将
!function(){
  function logMe(){
    const token = localStorage.getItem("token");
    const someObj = {value: null};
    async function isUserSessionValid() {...}
    async function validateUserSession() {
      taskq.pause;
      if(token != null) {
        if(await isUserSessionValid()) {
            someObj.value = token;
            taskq.resume;
            return;
        }
      }
      ...
    }
    validateUserSession();
    taskq.export(someObj, "tokenObj");
  }
  logMe._taskqId = "first"
  taskq.push(logMe)
}()
中的所有内容放入名为

isUserLogged

 的函数中,并将其包装在 iife 中。我给它一个 
logMe
 首先并将其推入执行流程。我在验证函数中暂停/恢复了taskq。我还导出了一个您可以直接使用的变量。
本质上与
_taskqId

相同:

home.js

以上函数将等待
!function(){
  function home(tokenObj){
    //contents of your home.js
    ...
  }
  home._taskqId = "home";
  home._taskqWaitFor = ["first"]
}()
执行。我们之前导出的

first

 也可以在 
tokenObj
 中使用,因此 
home
 是您用户的 
tokenObj.value
。就是这样。
你也可以在
token

中使用taskq.load("./home.js")而不是

isUserLogged.js

,你有几个
选项
请注意,该解决方案将在包含的 ie10 上运行,并且也可以轻松地与现代 es6 模块结合使用。您始终可以推出类似于 taskq 的自己的解决方案,它们都围绕在执行之前存储函数。

免责声明:我是taskq的开发者。

© www.soinside.com 2019 - 2024. All rights reserved.