Как получить некоторые текстовые файлы перед инициализацией веб-страницы (=> синхронно) - PullRequest
0 голосов
/ 27 октября 2019

Я действительно хочу сделать простую вещь - загрузить несколько glsl фрагментных шейдеров с диска / сервера, а затем инициализировать WebGL-страницу. Я действительно не веб-программист, обычно я предпочитаю программировать шейдеры и opengl на C ++. Я бы никогда не положил руки на javascript, если бы не было WebGL. Я не осознавал, насколько сложно это было просто загрузить файл в javascript. Тогда кто-нибудь из переполнения стека рекомендует использовать fetch API , что кажется довольно удобным. Поэтому я попытался так:

// somewhere in HTML body far far away
<script>
var str_RayTracer  = "//RayTracer";
var str_Primitives = "//Primitives";
fetch('RayTracer.glslf') .then(response => response.text()).then(text => str_RayTracer  = text );
fetch('Primitives.glslf').then(response => response.text()).then(text => str_Primitives = text );
init_GLSLScreen(str_Primitives, str_RayTracer); 
</script>

Проблема в том, что fetch - это какая-то асинхронная фигня, поэтому init_GLSLScreen(str_Primitives, str_RayTracer) выполняется до загрузки шейдеров => Выдает исключение, например cannot compile shaders and stuff .... Я действительно не хочу увлекаться этим асинхронным бизнесом, и Я хочу вернуться - к приличному синхронному программированию - как можно быстрее . потому что 1) отладка асинхронных вещей - ад, и 2) потому что программе действительно нечего делать, пока не загрузятся шейдеры.

Кажется, нет простого способа синхронизировать асинхронные и синхронные вызовы в javascript (что странно - на каждом параллельном языке программирования, который я знаю, у вас есть точки синхронизации). Нет ничего похожего на wait_to_resolve, есть просто await , который (несмотря на то, что предполагает название) на самом деле ничего не ждет. Это только продвигает проблему дальше, заставляя вас также выполнять функцию оболочки async.

Я могу заставить его работать следующим образом:

<script>
var str_RayTracer  = "//RayTracer";
var str_Primitives = "//Primitives";
function initThis(txt){
   // for some reason I must take `txt` although I don't use it
   init_GLSLScreen(str_Primitives, str_RayTracer);
} 

async function loadPage(){
    str_Primitives = await fetch('Primitives.glslf').then( r => r.text() );
    str_RayTracer  = await fetch('RayTracer.glslf' ).then( r => r.text() );
    return 0; // for some reason I must return something
}

loadPage().then( txt => initThis(txt) );
//loadPage().then( initThis() ); // doesn't seem to work
</script>

Но мне это не нравится, так как инициализация является последовательной операцией. Я не понимаю, почему я не могу написать нормальный последовательный код, и вместо этого должен цеплять обратные вызовы, как это. Это похоже на написание последовательной программы в обратном направлении.

Я пытался прочитать все потоки об этой проблеме при переполнении стека (например, 1 2 ), и это не очень полезноответ. Есть просто «О, мы здесь этого не делаем» мем . Лучше всего выражается здесь: Как мне вернуть ответ от асинхронного вызова?

Везде написано что-то вроде There is no way how to do it without blocking GUI. Ок, и что? Подскажите как это сделать with blocking the GUI, пожалуйста! Поскольку графический интерфейс пользователя не может быть даже инициализирован до тех пор, пока я не загружу эти файлы.

Я понимаю, что иногда неблокирующие и асинхронные операции полезны, особенно когда вы хотите сделать отзывчивую веб-страницу. Но иногда нет, иногда вы действительно хотите подождать, пока работа не будет выполнена, и выбор должен быть за программистом.

1 Ответ

0 голосов
/ 27 октября 2019

Подскажите, пожалуйста, как это сделать с блокировкой графического интерфейса!

Используйте XMLHttpRequest, который поддерживает синхронный режим, вместо fetch.

Но на самом деле вы не должны этого делать, это по сути устарело. Узнайте, как использовать асинхронное программирование для Интернета :-) Вы были довольно близки на самом деле. Асинхронный, но полностью последовательный код выглядел бы так - без глобальных переменных для результатов обещания:

<script>
async function loadTextfile(path) {
    var response = await fetch(path);
    return response.text();
}
async function loadPage() {
    var str_Primitives = await loadTextfile('Primitives.glslf');
    var str_RayTracer  = await loadTextfile('RayTracer.glslf');

    init_GLSLScreen(str_Primitives, str_RayTracer);
}

loadPage().catch(err => { console.error(err); });
</script>

Теперь вы можете очень легко повысить производительность, загружая два файла одновременно - просто измените на

async function loadPage() {
    var [str_Primitives, str_RayTracer] = await Promise.all([
        loadTextfile('Primitives.glslf'),
        loadTextfile('RayTracer.glslf'),
    ]);
    init_GLSLScreen(str_Primitives, str_RayTracer);
}
...