如何通过外部命令停止执行异步处理

问题描述 投票:0回答:3

我希望这段代码能说明一切第一个按钮启动异步命令(在此示例中为setTimeout),第二个按钮允许在旅途中停止其执行。

[除了我的代码中没有像我想象的那样,而且我承认,我仍然很难满足诺言...。

目前我不明白为什么setTimeout没有效果?

我(仍然)想念什么?

(function(messageBox)
  {
  const texBox = document.getElementById('message-box')
    ;
  messageBox.txt = txt => texBox.textContent = txt 
  messageBox.add = txt => texBox.textContent += '\n'+txt
  }
(window.messageBox=window.messageBox || {}));

function timer()
{
  let status = 'none'  // inUse 
    , obj    = document.createElement('b')
    , ref    = null
    , msTime = 0
    ;
  const sleep  =_=> new Promise(res => ref=setTimeout(res, ms))
    ,   cancel =_=> new Promise(res => {
          obj.onclick =_=> {
            if (status==='inUse') {
              clearTimeout(ref)
              res()
        } } })
  async function start( ms ) {
    if (status==='none') {
      msTime = ms 
      status = 'inUse'
      await Promise.race([sleep,cancel])
      status = 'none'
    }
  }        
  function stop() { if (status==='inUse') obj.click() }

  return( { start, stop} )
}

const btStart = document.getElementById('bt-start')
  ,   btAbort = document.getElementById('bt-abort')
  ,   OnDelay = timer()
  ;
btStart.onclick=_=>
  {
  messageBox.txt('Start waitting 5 seconds...')
  OnDelay.start(5000)
  messageBox.add('finish waitting :)')
  }
btAbort.onclick=_=>
  {
  OnDelay.stop()
  messageBox.add('...abort !')
  }
#message-box {
  margin: 1em;
  border: 1px solid lightskyblue;
  width: 20em;
  height: 10em;
}
<button id="bt-start">wait 5s</button>
<button id="bt-abort">abort</button>
<pre id="message-box">message...</pre>

这个想法是:2个选择a)单击按钮wait 5s => messageBox将立即显示Start waiting 5 seconds...5秒后,程序将添加finish wait:)messageBox =

[开始等待5秒钟...完成等待:)

(我不希望它起作用)

b)单击按钮wait 5s => messageBox = Start waiting 5 seconds...(此时与= a =相同)

然后单击之前按钮abort的5秒结束=> messageBox =

[开始等待5秒钟......中止!完成等待:)

在5秒钟之前(它也无法如我所愿)

javascript promise
3个回答
0
投票

我在下面添加了一些简单的代码,它们可以模拟您所需的执行,并有助于解决您的查询。

(() => {
  const messageBox = document.getElementById('message-box');       
  const btStart = document.getElementById('bt-start');
  const btAbort = document.getElementById('bt-abort');
  const interval = 5000;
  
  let objTimeout = null;  
  btStart.onclick = () => {
    messageBox.textContent += `\n [-] start waiting ${interval} ms...`;
    objTimeout = setTimeout(()=>{
    	messageBox.textContent += `\n [-] message  after ${interval} ms`;
    },interval)
  }
  btAbort.onclick = () => {  
    if(objTimeout){
    	clearTimeout(objTimeout);
      messageBox.textContent += `\n [x] printing aborted`;
    	messageBox.textContent += `\n [x] finish waiting`;
    }
  }

})();
#message-box {
  margin: 1em;
  border: 1px solid lightskyblue;
  width: 30em;
  height: 10em;
}
<button id="bt-start">wait 5s</button>
<button id="bt-abort">abort</button>
<pre id="message-box">waiting for message...</pre>

================================================ ==================我已在下面的摘要中发布了承诺。仅当您想要同步代码或防止竞争条件或想要逐行执行语句时,才需要执行相同的promise。希望它能对问题有所启发并帮助解决。

(() => {
  const messageBox = document.getElementById('message-box');
  const btStart = document.getElementById('bt-start');
  const btAbort = document.getElementById('bt-abort');
  const interval = 5000;
  let objTimeout = null;
  let objPromise = null;

  btStart.onclick = () => {
    return new Promise((resolve, reject) => {
      objTimeout = setTimeout(() => {
        objPromise = Promise.resolve().then(() => {
          messageBox.textContent += `\n [-] start waitting ${interval} seconds...`;
        }).then(() => {
          messageBox.textContent += `\n [-] message  after ${interval} ms`;
        }, interval).then(() => {
          messageBox.textContent += `\n [-] printing finished`;
          resolve();
        });
      }, interval);
    });
  }


  btAbort.onclick = () => {
    if(objTimeout !== null){
      Promise.reject(new Error("promise aborted"));
      clearTimeout(objTimeout);
      messageBox.textContent += `\n [x] printing aborted`;
      messageBox.textContent += `\n [x] finish waiting`;
      objTimeout= null;
    }    
  }
})();
#message-box {
  margin: 1em;
  border: 1px solid lightskyblue;
  width: 30em;
  height: 10em;
}
<button id="bt-start">wait 5s</button>
<button id="bt-abort">abort</button>
<pre id="message-box">waiting for message...</pre>

0
投票

我认为您正在考虑问题。

您需要的是one Promise及其通常的.then.reject.finally()。不需要.race

此外,您还可以将所有这些操作包装到一个类中,作为一种更干净,可重用的方法:

const messageBox = document.getElementById('message-box');
const btStart = document.getElementById('bt-start');
const btAbort = document.getElementById('bt-abort');

class Countdown {
  constructor(onStart, onEnd, onAbort){
    this.timeout = null;
    this.handleCatch = onAbort;
    this.handleFinally = onEnd;
    this.starter = (res, rej) => {
      onStart();
      this.timeout = setTimeout(res, 5000);
      // Pass the `reject` function to a property to be used later
      this.aborter = rej;
    };
  }
  
  async start(){
    try {
      // Only one `Promise` is needed.
      this.main = await new Promise(this.starter);
    } catch(e){
      /* This will run on abort */
      // Stop the timeout
      clearTimeout(this.timeout);
      // Reset the property to make sure `abort` can only be run once for each valid timeout.
      this.aborter = null;
      this.handleCatch();
    } finally {
      /* This will run after everything ends */
      this.handleFinally();
    }
  }
  
  abort(){
    if (typeof this.aborter === 'function'){
      // Use the `reject` that is passed to this property earlier.
      this.aborter();    
    }
  }
}

const countdown = new Countdown(
  // Run when countdown is started. See `onStart` in the class consturctor.
  () => messageBox.txt('Start waiting 5 seconds...'),
  // Run when countdown is ended. See `onEnd` in the class consturctor.
  () => messageBox.add('finish waiting :)'),
  // Run when countdown is aborted. See `onAbort` in the class consturctor.
  () => messageBox.add('...abort!')
)

messageBox.txt = text => messageBox.textContent = text;
messageBox.add = text => messageBox.textContent += '\n' + text;

btStart.addEventListener('click', countdown.start.bind(countdown));
btAbort.addEventListener('click', countdown.abort.bind(countdown));
#message-box {
  margin: 1em;
  border: 1px solid lightskyblue;
  width: 20em;
  height: 10em;
}
<button id="bt-start">wait 5s</button>
<button id="bt-abort">abort</button>
<pre id="message-box">message...</pre>

这样,不仅代码是干净的,而且可以在代码中的任何地方重复使用倒数计时。

您要做的就是告诉实例要执行的操作onStartonEndonAbort


0
投票

使用异步功能:

(function(messageBox) {
    const texBox = document.getElementById('message-box');
    messageBox.txt = txt => texBox.textContent = txt
    messageBox.add = txt => texBox.textContent += '\n' + txt
  }
  (window.messageBox = window.messageBox || {}));

function timer() {
  let timerID, rejectPromise;

  function start(nMS) {
    return new Promise((resolve, reject) => {
      timerID = setTimeout(_ => {
        timerID = undefined;
        resolve();
      }, nMS);
      rejectPromise = reject;
    });
  }

  function stop() {
    timerID && clearTimeout(timerID);
    timerID = undefined;
    rejectPromise();
  }

  return ({
    start,
    stop
  })
}

const btStart = document.getElementById('bt-start'),
  btAbort = document.getElementById('bt-abort'),
  OnDelay = timer();
  
btStart.onclick = async _ => {
  messageBox.txt('Start waitting 5 seconds...')
  try {
    await OnDelay.start(5000);
  } catch (_) {}
  messageBox.add('finish waitting :)')
}

btAbort.onclick = _ => {
  OnDelay.stop()
  messageBox.add('...abort !')
}
#message-box {
  margin: 1em;
  border: 1px solid lightskyblue;
  width: 20em;
  height: 10em;
}
<button id="bt-start">wait 5s</button>
<button id="bt-abort">abort</button>
<pre id="message-box">message...</pre>

注意:要等待5秒,然后then做某事,您必须

  1. 有一个诺言p,5秒钟后由setTimeout解决,另一行说p.then(function() { doSomething() })
  2. 或使用异步功能,并使用await进行上述(1)中的保证。

由于即使您拒绝了诺言,您也希望执行相同的操作,所以我在try... catch上添加了await。否则将引发错误,并且await下面的代码将不会继续。

在您的原始代码中,您调用了OnDelay.start(5000),它将一直运行到await行,以等待该诺言得到解决,并且控件返回到messageBox.add('finish waitting :)')的下一行,并且它将立即显示。它不像同步程序,您可以在其中“睡眠” 5秒钟。仅当您在异步函数中完成所有操作并且您在5秒钟内解决await promise然后执行某项操作,或者使用诺言然后在5秒内解决并将其设置为promise.then(doSomething)时,才有可能。 >

使用承诺而不是异步功能:

由于即使希望被拒绝,您也希望执行相同的操作,所以您将使用finally而不是then

(function(messageBox) {
    const texBox = document.getElementById('message-box');
    messageBox.txt = txt => texBox.textContent = txt
    messageBox.add = txt => texBox.textContent += '\n' + txt
  }
  (window.messageBox = window.messageBox || {}));

function timer() {
  let timerID, rejectPromise;

  function start(nMS) {
    return new Promise((resolve, reject) => {
      timerID = setTimeout(_ => {
        timerID = undefined;
        resolve();
      }, nMS);
      rejectPromise = reject;
    });
  }
  
  function stop() {
    timerID && clearTimeout(timerID);
    timerID = undefined;
    rejectPromise();
  }

  return ({
    start,
    stop
  })
}

const btStart = document.getElementById('bt-start'),
  btAbort = document.getElementById('bt-abort'),
  OnDelay = timer();
  
btStart.onclick = _ => {
  messageBox.txt('Start waitting 5 seconds...')
  OnDelay.start(5000).finally(_ => messageBox.add('finish waitting :)'));
}

btAbort.onclick = _ => {
  OnDelay.stop()
  messageBox.add('...abort !')
}
#message-box {
  margin: 1em;
  border: 1px solid lightskyblue;
  width: 20em;
  height: 10em;
}
<button id="bt-start">wait 5s</button>
<button id="bt-abort">abort</button>
<pre id="message-box">message...</pre>
© www.soinside.com 2019 - 2024. All rights reserved.