Репозиторий Github: https://github.com/leighhobson89/ChipshopChopper
Мне трудно заставить один файл .js
запустить свой код до того, как это попытается сделать другой. Суть проблемы в том, что я вызываю функцию через событие отправки в одном файле (давайте вызовем ее 1.js
), а вызываемая ею функция находится во втором файле (давайте вызовем ее 2.js
). Однако, несмотря на то, что функция вызывается при срабатывании диспетчеризации событий, переменная, в которую она должна записываться в этой функции, возвращается неопределённой (она объявлена над функцией в 2.js
).
Я попытался объявить эту переменную в 1.js
перед отправкой события и его экспортом, но затем получил сообщение об ошибке, сообщающее, что вы не можете назначить константную переменную. Весь смысл этого упражнения в том, чтобы иметь эту константу в отдельном файле, потому что это большой длинный список элементов DOM, и я не хочу продолжать вызывать document.getElementById()
каждый раз, когда хочу повлиять на innerHTML
элемента, поэтому я создал событие для запуска вызова этой функции в том месте кода, где я хочу, чтобы она загрузила ее, т. е. после создания элементов, но до того, как их запросят на изменение.
Мне нужен способ, кроме defer
или сортировки порядка в HTML или помещения скрипта в тег <head>
(все это я пробовал безуспешно), заставить его инициализировать переменную в 2.js
, прежде чем что-либо делать в 1.js
.
Я предоставлю примеры кода, если кто-то захочет их увидеть, но трудно получить контекст, не вставив огромные фрагменты кода из нескольких файлов, поэтому заранее приношу извинения за это.
Из онлайн-исследований я попробовал:
defer
чтобы попробовать сделать 1.js
загрузку перед 2.js
1.js
в тег <head>
Promise
в функции, которая устанавливает переменную elements, чтобы разрешить, когда элементы имеют значение, отличное от нуля или неопределенного (но у меня нет хороших навыков асинхронного программирования, так что, возможно, это было правильно или нет)<script>
в HTML-файле.1.js:
let elements;
export function setElements() {
elements = {
option1: document.getElementById('option1'),
option2: document.getElementById('option2'),
//etc
}
2.js
import {getElements, setElements} from './1.js'
document.addEventListener('titleScreenCreated', setElements);
const titleScreenCreatedEvent = new Event('titleScreenCreated');
createGameWindow(titleScreenCreatedEvent);
//code that creates various elements
document.dispatchEvent(titleScreenCreatedEvent);
console.info(getElements());
//code that changes elements
// getElements().option1.innerHTML = "hello world";
index.html
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF-8">
<meta name = "viewport" content = "width=device-width, initial-scale=1.0">
<link rel = "stylesheet" href = "styles.css">
<title>Your Game</title>
</head>
<body>
<div class = "clock">00:00:00</div>
<script type = "module" src = "actions.js"></script>
<script defer type = "module" src = "1.js"></script>
<script type = "module" src = "ui.js"></script>
<script type = "module" src = "2.js"></script>
</body>
</html>
@Yousaf Я добавил несколько упрощенных примеров и весь файл index.html.
При нынешней настройке он никогда не будет заполнен правильно, поскольку все ваши модули совместно используются только в том случае, если они происходят из одного корня модуля. Поэтому, если вы импортируете оба файла в единый файл импорта, он может начать работать. Прямо сейчас импорт 1.js с использованием тега script
, который загружается в 2.js
, будет отдельным экземпляром из 2.js
, загруженного с помощью тега script
.
Привет, Ли, добро пожаловать в StackOverflow! Я не думаю, что проблема в том, где вы предполагаете. Я запустил ваш код (после нескольких исправлений синтаксиса), и он печатает элементы getElements
так, как и ожидалось. К сожалению, вам придется глубоко погрузиться в эти «огромные участки кода» с помощью отладчика, чтобы выяснить, что происходит не так. Я предлагаю вам поместить точки останова в установщик и геттер в качестве первого шага, чтобы проверить порядок выполнения.
@somethinghere Он загрузился только один раз, когда я тестировал в Chrome
@somethinghere спасибо за ответ. К сожалению, я не смог в полной мере понять то, на что вы намекали. Не могли бы вы написать пару строк псевдокода, которые дали бы мне представление о том, как мне следует изменить структуру импорта и в каких файлах это сделать?
@TamasHegedus спасибо за ответ. Возможно, вы правы, но большая часть не включенного кода — это просто создание элементов. Ошибка указывает, что в этом порядке переменная elements не может быть доступна до инициализации. И, как это заявлено, я могу только предположить (а также подкрепить точками останова, чтобы увидеть порядок выполнения), что он обращается к функции setElements() через событие, прежде чем читать что-либо из файла 1.js (что, кстати, это файл ConstantsAndGlobalVars.js, в котором я храню все свои константы и методы получения/установки.)
Я добавил ссылку на репозиторий GitHub в пост, как и сейчас, если у кого-то есть время запустить его, просто чтобы посмотреть. github.com/leighhobson89/ChipshopChopper
Сначала вам следует удалить циклы. Следует избегать циклических зависимостей.
@somethinghere Почему ты так думаешь? Я протестировал его, и Chrome, похоже, не создает параллельные графы модулей, а сводит их в один большой граф, где каждый модуль инициализируется только один раз, как я и ожидал.
Странно, я был уверен, что так и было раньше. Только что протестировал его, и он действительно делает это иначе, чем я использовал. Приятно это знать, но это немного странно, клянусь, из-за этого я уже сталкивался с подобной проблемой. Ах хорошо.
Спасибо за ссылку на репозиторий GitHub, таким образом я смог воспроизвести вашу проблему.
Ваша игра пытается создать пользовательский интерфейс, когда модули еще не полностью загружены. Я предлагаю создать функцию main
, которая будет служить точкой входа для вашего приложения, и перенести всю инициализацию игры в эту функцию. Обязательно вызывайте его после инициализации всех модулей, например, используйте событие DOMContentLoaded.
function main() {
document.addEventListener('titleScreenCreated', settlements);
const titleScreenCreatedEvent = new Event('titleScreenCreated');
createGameWindow(titleScreenCreatedEvent);
createTitleScreen();
gameLoop();
}
document.addEventListener('DOMContentLoaded', () => {
main();
});
В более общем плане, вы видите проблемы такого рода только тогда, когда существуют циклы зависимостей. Когда это происходит, нет надежного способа контролировать порядок загрузки, приходится реструктурировать приложение. Лучший способ обойти эту проблему — вообще избегать циклов, если это возможно.
Проблема решена и получен хороший урок! Отлично, спасибо за помощь!
Трудно понять вашу проблему, не видя упрощенного примера кода.