我有一个函数,在某些时候做document.getElementsByTagName("script")
,它想知道每个脚本是否已经加载。
我想得到一个承诺,如果它被加载和挂起,如果它仍然在进行,那么我可以用.then(...)
附加下一步,并确保它们被执行。
我想我可以附加一个加载事件,但是如果脚本已经加载了,我就永远不会得到这个事件。
我可以通过做一些假设来解决这个问题,即我控制所有动态脚本标签,对于静态脚本标签,文档。currentScript总是到目前为止加载的最后一个脚本,所有前面的脚本都已经加载完成,假设它们都是静态的。但这些假设可能并不总是正确的。
那么,是否有一种方法来检查状态并生成承诺,根据状态将开始作为完成(解决)或由加载事件触发完成?
添加脚本元素的DOM将导致异步加载,没有立即在代码之前使用它们。您需要为脚本的onload
事件使用回调函数。下面是这样做的一个示例帮助函数。
function loadAsync(url, callback) {
var s = document.createElement('script');
s.setAttribute('src', url);
s.onload = callback;
document.head.insertBefore(s, document.head.firstElementChild);
}
MutationObserver
可用于为动态创建的脚本元素添加加载事件侦听器。
Map
用于存储加载事件时承诺,将解决火灾。
const scriptMap = new Map();
const observer = new MutationObserver(mutations => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeName === 'SCRIPT' && node.getAttribute('src') != null) {
scriptMap.set(node, new Promise(resolve => {
node.addEventListener('load', () => resolve());
}));
}
}
}
});
开始观察:
observer.observe(document, {
childList: true,
subtree: true
});
定义一个函数,如果脚本还没有加载或解析,返回一个承诺,该承诺将挂起:
const load = script => scriptMap.get(script) ?? Promise.resolve();
使用例子:
const scriptElements = document.getElementsByTagName('script');
for (let script of scriptElements) {
await load(script);
}
使用scriptMap.values()
获取动态创建脚本的承诺:
await Promise.all(scriptMap.values());
为了效率起见,当不再需要时停止观察:
observer.disconnect();