我希望这段代码能说明一切
第一个按钮启动异步命令(在此示例中为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('next things to do...')
}
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秒钟之前 (它也不如我所愿)
答案 0 :(得分:1)
(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秒,然后 然后 做某事,您需要
p
,5秒钟后由setTimeout
解决,另一行说p.then(function() { doSomething() })
await
来实现上述(1)中的承诺。由于即使希望被拒绝,您也希望执行相同的操作,因此我在try... catch
上添加了await
。否则将引发错误,并且await
下面的代码将不会继续。
在您的原始代码中,您调用了OnDelay.start(5000)
,它将一直运行到await
行,以等待该诺言得到解决,并且控件返回到{{1}的下一行},它将立即显示。它不像一个同步程序,您可以在其中“睡眠” 5秒钟。仅当您在异步函数中完成所有操作并且messageBox.add('finish waitting :)')
在5秒钟内解决,然后执行某项操作,或者使用诺言然后在5秒钟内解决,并将其设置为{{1 }}。
您的原始问题:
如何通过外部命令停止执行异步处理
有一个诺言等待解决,有await promise
可以在5秒后解决。因此,您可以清除计时器,并拒绝承诺。如果您不拒绝(或解决)它,诺言将永远存在。 promise.then(doSomething)
不会继续,或者setTimeout
之后的行也不会继续。如果您拒绝诺言,则由then
或await
或then
提供的第二个处理程序来处理诺言。如果您使用异步功能,则该catch
将引发错误,您可以使用finally
来处理或忽略它。
由于即使希望被拒绝,您也希望执行相同的操作,因此请使用await
而不是try-catch
。
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).catch(err => {}).finally(_ => messageBox.add('finish waitting :)'));
}
btAbort.onclick = _ => {
OnDelay.stop()
messageBox.add('...abort !')
}
答案 1 :(得分:1)
我在下面添加了一些简单的代码,它们可以模拟您所需的执行,并有助于解决您的查询。
(() => {
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>
================================================ =================== 我在下面的摘要中发布了承诺。 仅当您想同步代码或防止竞争条件,或者想逐行执行语句时,才需要执行相同的诺言。
如果我们不确定在这种情况下诺言是否会解决或拒绝,我们应该使用setTimeout并解决或拒绝诺言。
我希望它能使您对此问题有所了解并有助于解决。
(() => {
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;
const appendMessage = (message) => {
messageBox.textContent += message;
}
const addMessage = (message) => {
messageBox.textContent = message;
}
const fnPromiseResolve = () => {
console.log("promise resolved");
};
const fnPromiseReject = () => {
clearTimeout(objTimeout);
appendMessage(`\n [x] printing aborted`);
appendMessage(`\n [x] finish waiting`);
objTimeout = null;
};
btStart.onclick = () => {
objTimeout = setTimeout(() => {
Promise.resolve().then(() => {
addMessage(`\n [-] start waitting ${interval} ms`);
}).then(() => {
appendMessage(`\n [-] message after ${interval} ms`);
}, interval).then(() => {
appendMessage(`\n [-] printing finished`);
objTimeout = null;
});
}, interval);
}
btAbort.onclick = () => {
if (objTimeout !== null) {
Promise.reject(new Error('printing aborted'))
.then(fnPromiseResolve, fnPromiseReject);
}
}
})();
#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>
答案 2 :(得分:1)
我认为您对这个问题的想法过多。
您只需要一个 Promise及其通常的.then
,.reject
和.finally()
。不需要.race
。
此外,您还可以将所有这些操作包装到一个类中,作为一种更干净,可重用的方法:
const btStart = document.getElementById('bt-start')
, btAbort = document.getElementById('bt-abort')
;
btAbort.disabled = true
;
const messageBox = document.getElementById('message-box')
;
messageBox.txt = txt => messageBox.textContent = txt;
messageBox.add = txt => messageBox.textContent += '\n' + txt;
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(
/* onStart */ ()=> {
btAbort.disabled = false
messageBox.txt('Start waiting 5 seconds...')
},
/* onEnd */ ()=> {
btAbort.disabled = true
messageBox.add('next things to do...')
},
/* onAbort */ ()=> messageBox.add('...abort!')
)
btStart.addEventListener('click', countdown.start.bind(countdown));
btAbort.addEventListener('click', countdown.abort.bind(countdown));
#message-box {
margin : 1em;
border : 1px solid lightskyblue;
padding: .3em;
width : 22em;
height : 5em;
}
<button id="bt-start">wait 5s</button>
<button id="bt-abort">abort</button>
<pre id="message-box">message...</pre>
这样,不仅您的代码是干净的,而且您可以在代码中的任何地方重用倒计时。
您需要做的就是告诉实例onStart
,onEnd
和onAbort
该做什么。
[edit(PO)]
添加了一些改进:
-禁用/启用中止按钮
-将消息从finish waiting :)
更改为next things to do...
,这样可以更清楚地了解这种确定性
+将样式更改为Whitesmiths样式,因为我发现可以更清晰地分析和理解代码。