当我有一个 Javascript 函数包含一个外部 Javascript 文件时,我怎样才能使函数块直到外部文件完全加载?
When I have a Javascript function include an external Javascript file, how can I make the function block until the external file is completely loaded?
首先声明这需要在纯香草Javascript中完成,没有第 3 方库或框架(强调不是JQuery).
假设我有一个名为 included_script.js
的 JS 文件,其中包含以下内容:
function sayIt() {
alert("Hello!");
}
现在假设我有以下简化的 JS 函数,它加载外部 JS 文件并尝试执行其中定义的 sayIt
函数:
function loadIt() {
var externalScript = document.createElement("script");
externalScript.type = "text/javascript";
externalScript.src = "js/included_script.js";
document.getElementsByTagName("head")[0].appendChild(externalScript);
/* BLOCK HERE, and do not continue until externalScript
(included_script.js) has been completely loaded from the server
and included into the document, so that the following execution of 'sayIt'
actually works as expected. */
sayIt(); /*I expect the "Hello!" alert here, but 'sayIt' is undefined (which
I think - but am not 100% sure - is because this line is reached before
externalScript (included_script.js) is fully downloaded from the server). */
}
请注意,在将 externalScript
附加到头部之前,我已经尝试过 externalScript.setAttribute("defer", "defer")
、externalScript.setAttribute("async", "async")
(尽管我知道这是多余的)等等。另请注意,回调不可行。
如何在上面显示的 "BLOCK HERE" 部分制作函数 loadIt
块,直到 externalScript
(included_script.js)
为 完全下载到客户端,使得externalScript
(included_script.js)
中定义的sayIt
函数在从函数loadIt
?[=36调用时实际工作=]
更新基于 BOBRODES 的精彩、简单的回答:
included_script.js
还有以下内容:
function sayIt() {
alert("Hello!");
}
loadIt
现在变成了 class(它比这复杂得多,但这显示了它工作所需的基本机制):
function loadIt() {
this.loadExternal = async function() {
return new Promise(
function(resolve, reject) {
try {
var externalScript = document.createElement("script");
externalScript.type = "text/javascript";
externalScript.src = "js/included_script.js";
if (externalScript.readyState) {
externalScript.onreadystatechange = function() {
if (externalScript.readyState == "loaded" ||
externalScript.readyState == "complete") {
externalScript.onreadystatechange = null;
resolve(true);
}
};
} else {
externalScript.onload = function() {
resolve(true);
};
}
document.getElementsByTagName("head")[0].appendChild(externalScript);
}
catch(err) {
reject(err);
}
}
);
}
}
现在,在我的主要代码中,我可以执行以下操作,并确保函数 sayIt
在调用之前已加载并准备好使用。
从异步函数内部:
var loader = new loadIt();
await loader.loadExternal();
sayIt();
从异步函数外部:
var loader = new loadIt();
(async function() {
await loader.loadExternal();
})().catch(err => {
console.error(err);
});
sayIt();
效果很好——正是我所追求的。谢谢,鲍勃!
附带说明一下,我知道有一种猖獗且目光短浅的 "blocking is always evil in every case imaginable, and can never, ever, under any circumstances, result in anything good" 心态,但我不同意当大量数据时阻塞是不好的-正在生成驱动的 GUI,这取决于多个自定义 classes,这些自定义 classes 又相互依赖 and/or 其他 classes/resources/scripts -- 特别是当呈现的 GUI 元素具有多个事件处理程序时(onclick
、oninput
、onfocus
等)期望这些 class 实例及其方法的 existence/usability。
如果你不能使用回调,那么使用promises,它旨在在异步环境中创建一个"blocking"机制,而无需添加单独的回调函数。
首先声明这需要在纯香草Javascript中完成,没有第 3 方库或框架(强调不是JQuery).
假设我有一个名为 included_script.js
的 JS 文件,其中包含以下内容:
function sayIt() {
alert("Hello!");
}
现在假设我有以下简化的 JS 函数,它加载外部 JS 文件并尝试执行其中定义的 sayIt
函数:
function loadIt() {
var externalScript = document.createElement("script");
externalScript.type = "text/javascript";
externalScript.src = "js/included_script.js";
document.getElementsByTagName("head")[0].appendChild(externalScript);
/* BLOCK HERE, and do not continue until externalScript
(included_script.js) has been completely loaded from the server
and included into the document, so that the following execution of 'sayIt'
actually works as expected. */
sayIt(); /*I expect the "Hello!" alert here, but 'sayIt' is undefined (which
I think - but am not 100% sure - is because this line is reached before
externalScript (included_script.js) is fully downloaded from the server). */
}
请注意,在将 externalScript
附加到头部之前,我已经尝试过 externalScript.setAttribute("defer", "defer")
、externalScript.setAttribute("async", "async")
(尽管我知道这是多余的)等等。另请注意,回调不可行。
如何在上面显示的 "BLOCK HERE" 部分制作函数 loadIt
块,直到 externalScript
(included_script.js)
为 完全下载到客户端,使得externalScript
(included_script.js)
中定义的sayIt
函数在从函数loadIt
?[=36调用时实际工作=]
更新基于 BOBRODES 的精彩、简单的回答:
included_script.js
还有以下内容:
function sayIt() {
alert("Hello!");
}
loadIt
现在变成了 class(它比这复杂得多,但这显示了它工作所需的基本机制):
function loadIt() {
this.loadExternal = async function() {
return new Promise(
function(resolve, reject) {
try {
var externalScript = document.createElement("script");
externalScript.type = "text/javascript";
externalScript.src = "js/included_script.js";
if (externalScript.readyState) {
externalScript.onreadystatechange = function() {
if (externalScript.readyState == "loaded" ||
externalScript.readyState == "complete") {
externalScript.onreadystatechange = null;
resolve(true);
}
};
} else {
externalScript.onload = function() {
resolve(true);
};
}
document.getElementsByTagName("head")[0].appendChild(externalScript);
}
catch(err) {
reject(err);
}
}
);
}
}
现在,在我的主要代码中,我可以执行以下操作,并确保函数 sayIt
在调用之前已加载并准备好使用。
从异步函数内部:
var loader = new loadIt();
await loader.loadExternal();
sayIt();
从异步函数外部:
var loader = new loadIt();
(async function() {
await loader.loadExternal();
})().catch(err => {
console.error(err);
});
sayIt();
效果很好——正是我所追求的。谢谢,鲍勃!
附带说明一下,我知道有一种猖獗且目光短浅的 "blocking is always evil in every case imaginable, and can never, ever, under any circumstances, result in anything good" 心态,但我不同意当大量数据时阻塞是不好的-正在生成驱动的 GUI,这取决于多个自定义 classes,这些自定义 classes 又相互依赖 and/or 其他 classes/resources/scripts -- 特别是当呈现的 GUI 元素具有多个事件处理程序时(onclick
、oninput
、onfocus
等)期望这些 class 实例及其方法的 existence/usability。
如果你不能使用回调,那么使用promises,它旨在在异步环境中创建一个"blocking"机制,而无需添加单独的回调函数。