J'espère que ce code parlera de lui-même
le premier bouton lance la commande asynchrone (ici un setTimeout pour l'exemple) le second bouton permet d'arrêter son exécution pendant le trajet.
Sauf que dans mon code ça ne se passe pas comme je l'imaginais, et j'avoue, j'ai encore du mal à être à l'aise avec les promesses ....
Pour le moment je le fais Vous ne comprenez pas pourquoi setTimeout n'a aucun effet?
Qu'est-ce que j'ai (encore) manqué?
<button id="bt-start">wait 5s</button> <button id="bt-abort">abort</button> <pre id="message-box">message...</pre>
#message-box { margin: 1em; border: 1px solid lightskyblue; width: 20em; height: 10em; }
(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 !') }
l'idée est: 2 options
a) cliquez sur le bouton attendez 5s
=> messageBox affichera immédiatement Start wait 5 seconds ...
et 5 secondes plus tard , le programme ajoutera finish wait :)
messageBox =
Attendez 5 secondes ...
prochaines choses à faire ...
(ça ne marche pas comme j'espère)
b) cliquez sur le bouton attendez 5s
=> messageBox = Attendez 5 secondes. ..
(idem = a = à ce moment)
puis cliquez sur avant la fin des 5 secondes sur le bouton abandonner
=> messageBox =
Attendez 5 secondes ...
... abandonner!
prochaines choses à faire ...
avant que les 5 secondes ne se soient écoulées (cela ne fonctionne pas non plus comme je l'espère)
3 Réponses :
<button id="bt-start">wait 5s</button> <button id="bt-abort">abort</button> <pre id="message-box">message...</pre>
#message-box { margin: 1em; border: 1px solid lightskyblue; width: 20em; height: 10em; }
(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 !') }
Remarque: pour que quelque chose attende réellement pendant 5 secondes et puis faire quelque chose, vous devez
p
, qui est résolue 5 secondes plus tard par setTimeout
, et une autre ligne pour dire p.then (function () {doSomething () })
attendez
une telle promesse comme dans (1) ci-dessus. Puisque vous voulez que la même action soit faite même si la promesse est rejetée, j'ai donc ajouté un try ... catch
au await
. Sinon, une erreur sera générée et le code sous wait
ne continuera pas.
Dans votre code d'origine, vous avez appelé OnDelay.start (5000)
et il fonctionnera jusqu'à la ligne wait
, pour attendre que cette promesse soit résolue, et le contrôle est de retour à la ligne suivante de messageBox.add ('finish waitting:)') code> et il sera affiché immédiatement. Ce n'est pas comme un programme synchrone où vous pouvez «dormir» pendant 5 secondes. Ce n'est possible que si vous faites tout cela dans une fonction asynchrone et que vous
attendez la promesse
qui se résout en 5 secondes, puis faites quelque chose, ou en utilisant une promesse puis résout en 5 secondes, et définissez-la sur soyez promise.then(doSomething)
.
Pour votre question initiale:
Comment arrêter l'exécution d'un traitement asynchrone par une commande externe
Il y a une promesse en attente d'être résolue, et il y a un setTimeout
pour la résoudre 5 secondes plus tard. Ainsi, vous pouvez effacer le chronomètre et rejeter la promesse. Si vous ne le rejetez pas (ou ne le résolvez pas), la promesse restera là pour toujours. Le puis
ne continuera pas, ou les lignes après wait
ne continueront pas non plus. Si vous rejetez la promesse, vous la manipulez par le second gestionnaire donné à puis
, ou par catch
, ou enfin
. Si vous utilisez la fonction asynchrone, alors cette wait
générera une erreur et vous pourrez utiliser try-catch
pour la gérer ou l'ignorer.
Puisque vous voulez que la même action soit effectuée même si la promesse est rejetée, vous utiliseriez enfin
au lieu de puis
.
<button id="bt-start">wait 5s</button> <button id="bt-abort">abort</button> <pre id="message-box">message...</pre>
#message-box { margin: 1em; border: 1px solid lightskyblue; width: 20em; height: 10em; }
(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 !') }
votre code n'affiche pas finish wait :)
from btStart
quand je clique sur abandonner ...: /
vous voulez dire même quand "abandonner", il doit montrer "terminer attendre"? Si c'est ce que vous voulez, c'est bien, je peux le changer ... mais généralement si vous "attendez" et que vous "abandonnez", alors le "wait" est annulé, et il ne devrait pas se terminer!
oui, c'est le but, je veux que le code arrivant après OnDelay.start (5000)
puisse toujours être exécuté
@MisterJojo ok J'ai changé les deux méthodes pour imprimer «terminé» même s'il est annulé.
merci d'avoir pris le temps de regarder ma question (+1). J'étudie ta réponse, mais il me faudra un peu de temps pour bien l'assimiler
J'ai ajouté ci-dessous un code simple qui peut simuler l'exécution souhaitée et aidera à résoudre votre requête.
<button id="bt-start">wait 5s</button> <button id="bt-abort">abort</button> <pre id="message-box">waiting for message...</pre>
#message-box { margin: 1em; border: 1px solid lightskyblue; width: 30em; height: 10em; }
(() => { 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); } } })();
=========================================== ======================== J'ai posté ci-dessous l'extrait avec promesse. Vous ne devez faire la même promesse que si vous souhaitez utiliser du code synchrone ou pour éviter la condition de concurrence critique ou si vous souhaitez exécuter l'instruction ligne par ligne.
Lorsque nous ne sommes pas certains que la promesse sera résoudre ou rejeter dans ce cas, nous devrions utiliser setTimeout et résoudre ou rejeter la promesse.
J'espère que cela éclairera le problème et aidera à le résoudre.
<button id="bt-start">wait 5s</button> <button id="bt-abort">abort</button> <pre id="message-box">waiting for message...</pre>
#message-box { margin: 1em; border: 1px solid lightskyblue; width: 30em; height: 10em; }
(() => { 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`; } } })();
Je mettrai également à jour avec promesse. donnez-moi quelques instants.
@MisterJojo, lorsque vous utilisez, promettez qu'il s'arrête uniquement lorsque le processus se termine. Il ne serait pas possible d'interrompre le processus entre les deux. Et votre cas est que vous souhaitez annuler la promesse entre le processus.
@MisterJojo Avez-vous un cas de condition de concurrence critique dans votre tâche et vous devez exécuter une instruction ligne par ligne, alors vous devriez utiliser promise. L'objectif principal de la promesse est d'éviter l'exécution asynchrone de l'instruction.
@MisterJojo c'est bon. N'hésitez pas à vérifier votre temps.
merci d'avoir pris le temps de regarder ma question (+1). J'étudie ta réponse, mais il me faudra un peu de temps pour bien l'assimiler
Je pense que vous réfléchissez trop au problème.
Tout ce dont vous avez besoin, c'est d'une une promesse et de son .puis
, .reject
habituel, et .finally ()
. Aucun .race
n'est nécessaire.
De plus, vous pouvez également envelopper toutes ces opérations dans une classe comme une approche plus propre et réutilisable:
<button id="bt-start">wait 5s</button> <button id="bt-abort">abort</button> <pre id="message-box">message...</pre>
#message-box { margin : 1em; border : 1px solid lightskyblue; padding: .3em; width : 22em; height : 5em; }
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));
De cette façon, non seulement votre code est propre, mais vous pouvez réutiliser le compte à rebours n'importe où dans votre code.
Tout ce que vous avez à faire est d'indiquer à l'instance ce qu'elle doit faire onStart
, onEnd
et onAbort
. p >
[modifier (PO)]
Ajout de quelques améliorations:
- désactiver / activer le bouton d'abandon
- changer le message de terminer l'attente :)
à prochaines choses à faire ...
qui est plus clair pour comprendre cette finalité
+ changer le style en style Whitesmiths, car je trouve plus clair d'analyser et de comprendre le code.
C'est un sacré bout de code! (J'ai beaucoup appris). J'ai continué à bricoler mon code un peu fou (petites améliorations), et votre approche est bien meilleure, dommage que vous ne pratiquiez pas le style Whitesmiths (je n'ai pas osé le changer)
@MisterJojo heureux que cela ait aidé. À propos du style de code, vous pouvez simplement le modifier en fonction de votre style. Cela ne cassera rien (théoriquement).
Merci pour la permission, j'espère que cela ne vous semblera pas trop horrible ... Sinon, votre code m'a aussi fait comprendre l'un de mes problèmes, je crée des promesses qui ne peuvent jamais finir (vous provoquez un rejet pour finir le promesse)
J'ai du mal à comprendre cette ligne: if (typeof this.aborter === 'function')
auriez-vous une référence ou une explication à ce sujet?
@MisterJojo cette ligne est juste pour vérifier si this.aborter
est une fonction. Nous devrions appeler this.aborter ()
uniquement lorsqu'il s'agit d'une fonction. Sinon, il lèvera une exception. Ceci est nécessaire car nous avons également une ligne this.aborter = null
lorsque nous annulons le compte à rebours.
Non, la promesse ES6 native n'est pas annulable, vous devez implémenter votre propre logique d'annulation ou utiliser certaines bibliothèques prenant en charge la promesse annulable, par exemple bluebird
@MarkoCen je peux voir ça, mais pour le moment je veux juste rendre mon exemple de code fonctionnel
@MisterJojo alors attendez-vous qu'après avoir cliqué sur le bouton «attendre 5s», il attendra 5 secondes avant que le message «terminer l'attente :)» ne soit imprimé? Vous avez non seulement des promesses, mais aussi une fonction asynchrone ... à ajouter à l'image
@MisterJojo alors qu'est-ce que tout le programme vise à faire? Juste pour avoir le bouton "attendre 5s" et il va attendre 5s et imprimer quelque chose, puis le bouton "abandonner" pour abandonner une telle action? (évidemment, si vous appuyez trop tard, cela n'aura aucun effet)
@MisterJojo cela élaborerait le but et l'explication du code alors ce serait bien que nous donnions une réponse plus précise
@MisterJojo plutôt que d'adopter une approche plus complexe, j'ai publié une réponse avec une approche simple, j'espère que cela vous aidera à résoudre votre problème et à comprendre le processus interne derrière la scène.