重置一个在承诺(promise)中的 setTimeout(),在点击事件(click event)发生时。

huangapple go评论61阅读模式
英文:

Reset a setTimeout() inside a promise, upon a click event

问题

这是我所面临问题的一个非常简化的重现:

window.onload = function () {
    touchme = document.getElementById('click');
    function mains() {
        touchme.innerHTML = "clicked " + Math.random().toString();
    }
    function process() {
        touchme.dataset.clicked = "false";
        mains();
        touchme.addEventListener("click", () => {
            touchme.dataset.clicked = "true";
        }, { once: true });

        let clickPromise = new Promise((resolve, reject) => {
            var timer = setTimeout(() => {
                if (touchme.dataset.clicked == "true") {
                    resolve();
                }
                else {
                    reject();
                }
            }, 1500);
        });

        clickPromise.then(() => {
            process();
        });

        clickPromise.catch(() => {
            alert("game over");
        });
    }

    process();
}

这段HTML代码中使用了 <body><button id="click">Click</button></body>

我基本上想要实现以下功能:

  • 如果我点击按钮,mains() 将立即运行。
  • 如果我没有点击,setTimeout() 将等待1.5秒。在此时间内,如果我点击,setTimeout() 会重置并执行 mains()
  • 如果我仍然没有点击,将执行 alert("game over")。"自我回调循环" 中断。

然而,无论何时我点击,mains() 函数都不会立即运行,而是等待超时,导致每次点击后都会有1.5秒的延迟。

解决方案通常是使用 clearTimeout(),但我似乎无法理解在哪里使用它。将其添加到事件监听器函数参数内部会导致定时器独立于点击事件运行,并在1.5秒后仅使其拒绝承诺,而不管我是否点击。我还尝试在事件监听器函数内部调用该函数本身,但这不起作用。将其添加到 if(touchme.dataset.clicked == "true") 之外的 setTimeout() 和承诺内部也不起作用,因为我的初始值是 false,所以它只会检查初始状态。

英文:

Here is a very simplified reproduction of the issue I am facing:

window.onload=function(){
    touchme = document.getElementById(&#39;click&#39;);
    function mains(){
        touchme.innerHTML = &quot;clicked &quot; + Math.random().toString();
    }
    function process() {
        touchme.dataset.clicked = &quot;false&quot;;
        mains();
        touchme.addEventListener(&quot;click&quot;, () =&gt; {
            touchme.dataset.clicked = &quot;true&quot;;
        }, {once : true});

        let clickPromise = new Promise((resolve, reject) =&gt; {
            var timer = setTimeout(() =&gt; {
                if(touchme.dataset.clicked == &quot;true&quot;){
                    resolve();
                }
                else{
                    reject();
                }
            }, 1500);
        });
        
        clickPromise.then(() =&gt; { 
            process();
        });

        clickPromise.catch(() =&gt; {
            alert(&quot;game over&quot;);
        });
    }

    process();
}

This bit of HTML has been used &lt;body&gt;&lt;button id=&quot;click&quot;&gt;Click&lt;/button&gt;&lt;/body&gt;

What I basically want is:

  • if I click the button, mains() will run immediately
  • if I don't, setTimeout() will wait 1.5secs for me. Within this time if I click, setTimeout() resets and mains() gets executed
  • if I still don't click, alert(&quot;game over&quot;) is executed. The "self callback loop" breaks

However, the mains() function isn't being run immediately whenever I am clicking, instead it is waiting for the timeout, causing a delay of 1.5s after every click.

Naturally, the solution is to use clearTimeout(), however, I can't seem to wrap my head around where to use it. Adding it inside the function argument of event listener causes the timeout to run independently of the click event and just makes it reject the promise 1.5s later, notwithstanding my button clicks. I also tried calling the function itself inside the event listener function, which doesn't work. Adding it inside an if(touchme.dataset.clicked == &quot;true&quot;) outside the setTimeout() and inside the promise wouldn't work, as my initial value is false, so it just checks the initial state.

答案1

得分: 3

你真的不需要使用 promises 来实现这个,只需一个简单的处理函数就可以更容易地清除和重置超时:

let lost = false;

function timeoutHandler() {
  alert("游戏结束");
  lost = true;
}

let timeout = setTimeout(timeoutHandler, 1500);
document.getElementById('click').addEventListener('click', () => {
  if (lost) return;
  clearTimeout(timeout);
  timeout = setTimeout(timeoutHandler, 1500);
});
<button id="click">点击</button>
英文:

You really don't need to use promises for this, just a simple handler function will make it a lot easier to clear and reset the timeout:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

let lost = false;

function timeoutHandler() {
  alert(&quot;game over&quot;);
  lost = true;
}

let timeout = setTimeout(timeoutHandler, 1500);
document.getElementById(&#39;click&#39;).addEventListener(&#39;click&#39;, () =&gt; {
  if (lost) return;
  clearTimeout(timeout);
  timeout = setTimeout(timeoutHandler, 1500);
});

<!-- language: lang-html -->

&lt;button id=&quot;click&quot;&gt;Click&lt;/button&gt;

<!-- end snippet -->

答案2

得分: 1

这是一个很好使用 Promise.race 的示例:

async function main() {
  while (await game()) {}
}

async function game() {
  let clickPromise = new Promise(res =>
    document.querySelector('button').onclick = () => res(true))

  let timerPromise = new Promise(res =>
    setTimeout(() => res(false), 1500))

  let ok = await Promise.race([clickPromise, timerPromise])

  document.querySelector('button').textContent = ok 
    ? 'keep clicking ' + Math.random() 
    : 'game over';

  return ok
}

window.onload = main
<button>click</button>
英文:

This makes a good use case for Promise.race:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

async function main() {
  while (await game()) {}
}

async function game() {
  let clickPromise = new Promise(res =&gt;
    document.querySelector(&#39;button&#39;).onclick = () =&gt; res(true))

  let timerPromise = new Promise(res =&gt;
    setTimeout(() =&gt; res(false), 1500))

  let ok = await Promise.race([clickPromise, timerPromise])

  document.querySelector(&#39;button&#39;).textContent = ok 
    ? &#39;keep clicking &#39; + Math.random() 
    : &#39;game over&#39;

  return ok
}

window.onload = main

<!-- language: lang-html -->

&lt;button&gt;click&lt;/button&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年1月9日 01:56:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/75050124.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定