如何在 fetch() 实际获取文件之前暂停 javascript 代码执行

Posted

技术标签:

【中文标题】如何在 fetch() 实际获取文件之前暂停 javascript 代码执行【英文标题】:How to pause javascript code execution until fetch() actually gets the file 【发布时间】:2021-08-17 06:34:24 【问题描述】:

假设页面上的标准进程以这样的window load 事件启动,

window.addEventListener("load",function()   doTheseFirst();   ,  once: true );

function doTheseFirst()

  setTimeout(function() andThenDoThese(); , 5000);
  // Maybe show some pictures and play some sounds for 5 seconds


function andThenDoThese()

  // Show other pictures and play other sounds until user clicks OK
  elementOnPageForOK.addEventListener("click", andThenDoTheseTooWhenUserClicksOK);


function andThenDoTheseTooWhenUserClicksOK()

  // etc etc
  // You get the idea

假设这段代码被反复使用。 但在某些情况下,用户必须在执行链开始之前看到通知。一个简单的解决方案是使用警报框,因为它会阻止/暂停代码执行。以下代码使fetch()txt 文件中获取内容时弹出警告框。请注意fetch()window load 是独立工作的。

fetch("somefolder/notification.txt").then(function(response)return response.text();).then(function(contentOfTheTxtFile) alert(contentOfTheTxtFile); );

之所以有效,是因为当警报框关闭时,主代码会根据需要返回其正常流程。

但是,如果不使用无法分配任何样式的粗略警告框,我该如何做同样的事情呢? 我可以让它在fetch() 获取文件后立即暂停代码执行,然后在用户单击 OKNEXT 之类的内容后立即取消暂停按钮?

这可以通过 async/await 实现吗?


EDIT1:进一步说明 请记住,不确定fetch()window load 是否会首先触发完成信号。因为下载速度是完全不可预测的。 因此,如果我们将它们都设置为这样,我们不知道另一个要经过多少毫秒才能赶上。

if(thisTimeItIsASpecialCase)
 fetch("somefolder/notification.txt").then(function(response)return response.text();).then(function(contentOfTheTxtFile) /*What to do when the file is ready*/ );

window.addEventListener("load",function()   doTheseFirst();   ,  once: true );

实现所需结果的简化方法:暂停doTheseFirst() 函数并使其等待fetch(),如果fetch() 确实在尝试获取文件,则让它完成获取文件的任务。也让doTheseFirst() 继续运行,以防没有可取的操作。

【问题讨论】:

我知道我们可以使用 setInterval 每 100 毫秒检查一次以查看 fetch 是否有结果,然后才触发 doTheseFirst() 和最后 clearInterval。但事实并非如此。 【参考方案1】:

您可以执行以下操作

你定义了一些 html 来显示你的内容并隐藏它直到你的内容真正可用

你定义了一个按钮来再次隐藏这个元素

next 事件处理程序只是用来调用 Promise 解析回调,该回调将在需要时定义

当您的文本可用时,可将其添加到您的显示器并创建等待 new Promise,单击下一步按钮即可解决此问题

promise 链中的下一个处理程序再次隐藏显示。

这个代码当然是非常基本的和示意性的。对于实际使用,您可以将其封装在自己的类或模块中。如果你正在使用像 React、Angular 等框架......有大量的组件可供使用,它们正好提供了这种功能。例如作为模态对话框或其他任何东西。

var resolver = undefined;

function next() 
  if (resolver) resolver.call();


function getText() 
  fetch("https://baconipsum.com/api/?type=meat-and-filler&paras=3&format=text")
    .then(response => response.text())
    .then(text => 
      document.getElementById("thetext").innerText = text;
      document.getElementById("display").hidden = false;
      return new Promise(resolve => 
        resolver = resolve;
      );
    )
    .then(() => 
      document.getElementById("display").hidden = true;
      resolver = undefined;
    )
    .catch(e => console.log(e));
<input type="button" onclick="getText()" value="Get Text">

<div id="display" hidden>
  <div id="thetext"></div>
  <input type="button" onclick="next()" value="OK">
</div>

【讨论】:

这是否会暂停 setTimeouts 和 setIntervals 的所有滴答声,直到用户单击“确定”?因为警报框可以做到这一点。 不,这不会暂停任何超时。唯一会做的是,只有在通过单击按钮实现承诺后才会隐藏文本区域。 如果您在fetchwindow.load 之间存在竞争条件,则说明您的应用程序存在设计问题。如果您有彼此独立启动的任务,则它们不应依赖于彼此的结果。如果其中一项任务需要其他结果,则只能在另一项任务完成后启动。但是从你对问题的描述,没人能看出,你真正需要的是什么…… 如果有另一种方法来控制事物的运行方式,我们就不必将其称为设计问题。如果没有任何东西可以做警报框可以做的事情,那么我当然必须改变代码的结构。 @TheLinguist 如果您根据您使用的语言规范正确地操作,您可以完美地控制事情的运行方式。顺便说一句,使用 alert 等待其他一些 JS 函数完成是行不通的。因为 JS 是单线程的。因此,在显示警报时,不会执行任何其他语句。【参考方案2】:

您可以将fetch 收到的文本文件的内容显示在漂亮的&lt;div&gt; 或提取中的任何元素中,然后退出函数。 您将EventListener 添加到一个按钮,当用户单击该按钮时,您可以移动到下一个操作。

【讨论】:

这很明显,但这不是对所问问题的回答。

以上是关于如何在 fetch() 实际获取文件之前暂停 javascript 代码执行的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 中的 Promises/Fetch:如何从文本文件中提取文本

如何从 fetch javascript 请求的响应中获取数据

如何防止在node.js中两次调用相同URL的获取

在服务停止完成之前暂停批处理文件?

Spark ShuffleExecutor是如何fetch shuffle的数据文件

如何限制在 Safari(MacOS 和 iOS)中暂停时获取 hls 段文件