首页 > 解决方案 > 如何在网页初始化之前获取一些文本文件(=>同步)

问题描述

我真的想做简单的事情——从磁盘/服务器加载一些glsl片段着色器,然后初始化 WebGL 网页。我真的不是网络程序员,通常我更喜欢用 C++ 编写着色器和 opengl。如果没有 WebGL,我永远不会使用 javascript。我没有意识到在 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!因为在我下载这些文件之前,甚至无法初始化 GUI。

我知道有时非阻塞和异步操作很有用,尤其是当您想要制作响应式网页时。但有时不是,有时你真的想等到工作完成,选择应该在程序员身上。

标签: javascriptasynchronouspromiseasync-awaitfetch

解决方案


请告诉我如何通过阻止 GUI 来做到这一点!

使用XMLHttpRequestwhich 支持同步模式,而不是fetch.

但实际上你不应该这样做,它基本上已被弃用。了解如何为 Web 采用异步编程 :-) 实际上你非常接近。异步但完全顺序的代码看起来像这样 - 避免 promise 结果的全局变量:

<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);
}

推荐阅读