Básicamente tengo dos funciones asíncronas. Uno de ellos es un tiempo de espera simple de 5 segundos y el otro es una función asíncrona complicada con varios pasos. Aquí hay un ejemplo
const delay = ms => new Promise(res => setTimeout(res, ms)); class Runner { async start() { let printStuff = async () => { for(let i = 0 ; i < 50; i++){ console.log(i); await delay(50); } } let printLetters = new Promise(async function(resolve, reject) { const length = Math.floor(Math.random() * 10) //dont know how long this will take for(let i = 0; i < length; i++){ await printStuff(); } resolve('letters'); }); let timeout = new Promise(async function(resolve, reject) { await delay(5000); resolve('timeout') }); const finished = await Promise.all([timeout, printLetters]); if(finished === 'timeout'){ this.stop(); } } stop(){ //stop printing stuff instantly } } const myRunner = new Runner(); myRunner.start(); //could call myRunner.stop(); if the user canceled itLa forma en que implementaría esto agregaría una variable global e incluiría una declaración if dentro del ciclo for para verificar si se ha llamado a la interrupción, pero me pregunto si hay una mejor manera de implementar esto. Un problema con esta solución sería que imprimiría algunos números más. Tendría que agregar otra verificación al otro bucle for y esto podría complicarse rápidamente.
Aquí hay una demostración simple que usa mi propia biblioteca.
import { CPromise } from "c-promise2"; const task = CPromise.promisify(function* () { let printStuff = CPromise.promisify(function* () { for (let i = 0; i < 10; i++) { console.log(i); yield CPromise.delay(100); } }); const length = Math.floor(Math.random() * 10) + 3; //dont know how long this will take for (let i = 0; i < length; i++) { yield printStuff(); } return "letters"; }); const promise = task() .timeout(5000) .then( (result) => console.log(`Done: ${result}`), (err) => console.warn(`Fail: ${err}`) ); setTimeout(() => { promise.cancel(); }, 2000);¿Es esto lo que buscas?
Qué cambió:
Promise.all reemplazado por Promise.raceisStopped prop que hace que la "función asíncrona complicada con múltiples pasos" omita la ejecución de los pasos restantes. aunque no lo mata. Las promesas no son cancelables . const delay = ms => new Promise(res => setTimeout(res, ms)); class Runner { isStopped = false; async start() { const printStuff = async () => { let i = 0; while (!this.isStopped) { console.log(i++); await delay(50); } } const printLetters = new Promise( resolve => printStuff() .then(() => resolve('letters')) ); const timeout = new Promise( resolve => delay(5000) .then(() => resolve('timeout')) ); const finished = await Promise.race([timeout, printLetters]); console.log({ finished }); if (finished === 'timeout') { this.stop(); } } stop() { this.isStopped = true; } } const myRunner = new Runner(); myRunner.start(); <button onclick="myRunner.stop()">stop</button>Respuesta inicial (lo dejó como lo mencionan los comentarios, no lo que está arriba; y en caso de que alguien lo encuentre útil en 2074 ):
Aquí hay un ejemplo que describe lo que estaba sugiriendo en el comentario. run() a continuación devuelve una carrera entre un rechazo que ocurre después de 1s y un cumplidor que se resuelve en un tiempo aleatorio entre 0 y 2s .
const rejector = (timeout) => new Promise((resolve, reject) => { setTimeout(reject, timeout, 'rejected') }); class Runner { run() { return Promise.race([ rejector(1000), new Promise((resolve, reject) => { setTimeout(resolve, Math.random() * 2000, 'fulfilled') }) ]) } } const t0 = performance.now(); [...Array(6).fill()].forEach((_, key) => { const runner = new Runner(); runner.run() .then(r => console.log(`Proomise ${key} ${r} after ${performance.now() - t0}ms`)) .catch(err => console.log(`Promise ${key} ${err} after ${performance.now() - t0}ms`)); })Nota: inicialmente coloqué el rechazo dentro de la clase pero (al menos para el ejemplo anterior) no veo por qué no debería permanecer fuera (en un caso real, importado de un archivo auxiliar).
Si necesita una capacidad de parada instantánea, probablemente desee ejecutar el trabajo de impresión como un script externo. Luego usa procesos secundarios como este.
const { spawn } = require('child_process'); class Runner { ...... start() { this.job[somejobId] = spawn('command to execute script'); //this can be anything, including a node script, eg `node start.js` ..... } stop(jobId) { if (jobId) { //this will kill the script that you spawn above this.job[jobId].kill('SIGHUP'); } } stopAllJobs() { // looping through the job queue to kill all the jobs this.job.forEach(job => job.kill('SIGHUP')) } }Tendrá más información sobre cómo iniciar un proceso secundario desde el sitio web de node doc https://nodejs.org/api/child_process.html#subprocesskillsignal
Si su trabajo (script externo) se está estancando , se recomienda que solo use los códigos anteriores si tiene un mínimo de 2 núcleos de CPU; de lo contrario, afectará su proceso principal si su script es pesado.