strip_tags remove from clean javascript promise cancellation es6-promise

javascript - remove - Cancelar una cadena de promesa ECMAScript 6 de vainilla



string to html javascript (13)

¿Hay algún método para borrar los .then s de una instancia de JavaScript Promise ?

He escrito un marco de prueba de JavaScript sobre QUnit . El marco ejecuta pruebas sincrónicamente ejecutando cada una en una Promise . (Perdón por la longitud de este bloque de código. Lo comenté lo mejor que pude, así que se siente menos tedioso).

/* Promise extension -- used for easily making an async step with a timeout without the Promise knowing anything about the function it''s waiting on */ $$.extend(Promise, { asyncTimeout: function (timeToLive, errorMessage) { var error = new Error(errorMessage || "Operation timed out."); var res, // resolve() rej, // reject() t, // timeout instance rst, // reset timeout function p, // the promise instance at; // the returned asyncTimeout instance function createTimeout(reject, tempTtl) { return setTimeout(function () { // triggers a timeout event on the asyncTimeout object so that, // if we want, we can do stuff outside of a .catch() block // (may not be needed?) $$(at).trigger("timeout"); reject(error); }, tempTtl || timeToLive); } p = new Promise(function (resolve, reject) { if (timeToLive != -1) { t = createTimeout(reject); // reset function -- allows a one-time timeout different // from the one original specified rst = function (tempTtl) { clearTimeout(t); t = createTimeout(reject, tempTtl); } } else { // timeToLive = -1 -- allow this promise to run indefinitely // used while debugging t = 0; rst = function () { return; }; } res = function () { clearTimeout(t); resolve(); }; rej = reject; }); return at = { promise: p, resolve: res, reject: rej, reset: rst, timeout: t }; } }); /* framework module members... */ test: function (name, fn, options) { var mod = this; // local reference to framework module since promises // run code under the window object var defaultOptions = { // default max running time is 5 seconds timeout: 5000 } options = $$.extend({}, defaultOptions, options); // remove timeout when debugging is enabled options.timeout = mod.debugging ? -1 : options.timeout; // call to QUnit.test() test(name, function (assert) { // tell QUnit this is an async test so it doesn''t run other tests // until done() is called var done = assert.async(); return new Promise(function (resolve, reject) { console.log("Beginning: " + name); var at = Promise.asyncTimeout(options.timeout, "Test timed out."); $$(at).one("timeout", function () { // assert.fail() is just an extension I made that literally calls // assert.ok(false, msg); assert.fail("Test timed out"); }); // run test function var result = fn.call(mod, assert, at.reset); // if the test returns a Promise, resolve it before resolving the test promise if (result && result.constructor === Promise) { // catch unhandled errors thrown by the test so future tests will run result.catch(function (error) { var msg = "Unhandled error occurred." if (error) { msg = error.message + "/n" + error.stack; } assert.fail(msg); }).then(function () { // resolve the timeout Promise at.resolve(); resolve(); }); } else { // if test does not return a Promise, simply clear the timeout // and resolve our test Promise at.resolve(); resolve(); } }).then(function () { // tell QUnit that the test is over so that it can clean up and start the next test done(); console.log("Ending: " + name); }); }); }

Si se agota el tiempo de espera de una prueba, mi Promesa de tiempo de espera se assert.fail() en la prueba para que la prueba se marque como fallida, lo cual está muy bien, pero la prueba continúa ejecutándose porque la Promesa de prueba ( result ) todavía está esperando para resolverlo

Necesito una buena forma de cancelar mi prueba. Puedo hacerlo creando un campo en el módulo de marco this.cancelTest o algo así y comprobando de vez en cuando (por ejemplo, al comienzo de cada iteración then() ) en la prueba si cancelar. Sin embargo, idealmente, podría usar $$(at).on("timeout", /* something here */) para borrar los restantes then() s en mi variable de result , de modo que no se ejecute el resto de la prueba .

Existe algo como esto?

Actualización rápida

Intenté usar Promise.race([result, at.promise]) . No funcionó.

Actualización 2 + confusión

Para desbloquearme, agregué algunas líneas con mod.cancelTest / polling dentro de la idea de prueba. (También eliminé el desencadenante del evento).

return new Promise(function (resolve, reject) { console.log("Beginning: " + name); var at = Promise.asyncTimeout(options.timeout, "Test timed out."); at.promise.catch(function () { // end the test if it times out mod.cancelTest = true; assert.fail("Test timed out"); resolve(); }); // ... }).then(function () { // tell QUnit that the test is over so that it can clean up and start the next test done(); console.log("Ending: " + name); });

Establecí un punto de interrupción en la declaración catch , y está siendo golpeado. Lo que me confunde ahora es que no se llama a la instrucción then() . Ideas?

Actualización 3

Descubrí lo último. fn.call() estaba arrojando un error que no at.promise.catch() , por lo que la promesa de prueba fue rechazada antes de que at.promise.catch() pudiera resolverlo.


¿Hay algún método para borrar los .then s de una instancia de JavaScript Promise?

No. No en ECMAScript 6 al menos. Las promesas (y sus manejadores) no se pueden cancelar de forma predeterminada (desafortunadamente) . Hay un poco de discusión sobre es-debate (por ejemplo, here ) sobre cómo hacer esto de la manera correcta, pero cualquier enfoque que gane no aterrizará en ES6.

El punto de vista actual es que las subclases permitirán crear promesas cancelables usando su propia implementación (no estoy seguro de qué tan bien funcionará) .

Hasta que el comité de idiomas haya descubierto la mejor manera (¿ES7 con suerte?) , Aún puede usar las implementaciones de Promesa de usuario, muchas de las cuales tienen cancelación.

La discusión actual se encuentra en los https://github.com/domenic/cancelable-promise y https://github.com/bergus/promise-cancellation draft.


Aquí está nuestra implementación https://github.com/permettez-moi-de-construire/cancellable-promise

Utilizado como

const { cancellablePromise, CancelToken, CancelError } = require(''@permettezmoideconstruire/cancellable-promise'') const cancelToken = new CancelToken() const initialPromise = SOMETHING_ASYNC() const wrappedPromise = cancellablePromise(initialPromise, cancelToken) // Somewhere, cancel the promise... cancelToken.cancel() //Then catch it wrappedPromise .then((res) => { //Actual, usual fulfill }) .catch((err) => { if(err instanceOf CancelError) { //Handle cancel error } //Handle actual, usual error })

cual :

  • No toca Promise API
  • Hagamos más cancelaciones dentro de catch call
  • Confíe en que se rechace la cancelación en lugar de resolverse a diferencia de cualquier otra propuesta o implementación

Tiradas y comentarios bienvenidos


En realidad, es imposible detener la ejecución de la promesa, pero puede secuestrar el rechazo y llamarlo desde la promesa misma.

function Sleep(ms,cancel_holder) { return new Promise(function(resolve,reject){ var done=false; var t=setTimeout(function(){if(done)return;done=true;resolve();}, ms); cancel_holder.cancel=function(){if(done)return;done=true;if(t)clearTimeout(t);reject();} }) }

Uso:

function cancelablePromise(cancel_holder,promise_fn,optional_external_cancel) { if(!cancel_holder)cancel_holder={}; return new Promise( function(resolve,reject) { var canceled=false; var resolve2=function(){ if(canceled) return; canceled=true; delete cancel_holder.cancel; resolve.apply(this,arguments);} var reject2=function(){ if(canceled) return; canceled=true; delete cancel_holder.cancel; reject.apply(this,arguments);} var on_cancel={} cancel_holder.cancel=function(){ if(canceled) return; canceled=true; delete cancel_holder.cancel; cancel_holder.canceled=true; if(on_cancel.cancel)on_cancel.cancel(); if(optional_external_cancel)optional_external_cancel(); reject(new Error(''canceled'')); }; return promise_fn.call(this,resolve2,reject2,on_cancel); }); } function Sleep(ms,cancel_holder) { return cancelablePromise(cancel_holder,function(resolve,reject,oncacnel){ var t=setTimeout(resolve, ms); oncacnel.cancel=function(){if(t)clearTimeout(t);} }) } let cancel_holder={}; // meanwhile in another place it can be canceled setTimeout(function(){ if(cancel_holder.cancel)cancel_holder.cancel(); },500) Sleep(1000,cancel_holder).then(function() { console.log(''sleept well''); }, function(e) { if(e.message!==''canceled'') throw e; console.log(''sleep interrupted'') })


Establezca una propiedad "cancelada" en la Promesa para señalar then() y catch() para salir temprano. Es muy efectivo, especialmente en Web Workers que tienen microtask existentes en cola en Promises de los controladores de onmessage .

// Queue task to resolve Promise after the end of this script const promise = new Promise(resolve => setTimeout(resolve)) promise.then(_ => { if (promise.canceled) { log(''Promise cancelled. Exiting early...''); return; } log(''No cancelation signaled. Continue...''); }) promise.canceled = true; function log(msg) { document.body.innerHTML = msg; }


Estoy realmente sorprendido de que nadie mencione Promise.race como candidato para esto:

const actualPromise = new Promise((resolve, reject) => { setTimeout(resolve, 10000) }); let cancel; const cancelPromise = new Promise((resolve, reject) => { cancel = reject.bind(null, { canceled: true }) }) const cancelablePromise = Object.assign(Promise.race([actualPromise, cancelPromise]), { cancel });



La respuesta de @Michael Yagudaev funciona para mí.

Pero la respuesta original no encadenó la promesa envuelta con .catch () para manejar el manejo de rechazos, aquí está mi mejora además de la respuesta de @Michael Yagudaev:

const makeCancelablePromise = promise => { let hasCanceled = false; const wrappedPromise = new Promise((resolve, reject) => { promise .then(val => (hasCanceled ? reject({ isCanceled: true }) : resolve(val))) .catch( error => (hasCanceled ? reject({ isCanceled: true }) : reject(error)) ); }); return { promise: wrappedPromise, cancel() { hasCanceled = true; } }; }; // Example Usage: const cancelablePromise = makeCancelable( new Promise((rs, rj) => { /*do something*/ }) ); cancelablePromise.promise.then(() => console.log(''resolved'')).catch(err => { if (err.isCanceled) { console.log(''Wrapped promise canceled''); return; } console.log(''Promise was not canceled but rejected due to errors: '', err); }); cancelablePromise.cancel();


Pruebe con promesa abortable : https://www.npmjs.com/package/promise-abortable

$ npm install promise-abortable

import AbortablePromise from "promise-abortable"; const timeout = new AbortablePromise((resolve, reject, signal) => { setTimeout(reject, timeToLive, error); signal.onabort = resolve; }); Promise.resolve(fn()).then(() => { timeout.abort(); });


Si bien no hay una forma estándar de hacer esto en ES6, hay una biblioteca llamada Bluebird para manejar esto.

También hay una forma recomendada descrita como parte de la documentación de reacción. Se parece a lo que tiene en sus actualizaciones 2 y 3.

const makeCancelable = (promise) => { let hasCanceled_ = false; const wrappedPromise = new Promise((resolve, reject) => { promise.then((val) => hasCanceled_ ? reject({isCanceled: true}) : resolve(val) ); promise.catch((error) => hasCanceled_ ? reject({isCanceled: true}) : reject(error) ); }); return { promise: wrappedPromise, cancel() { hasCanceled_ = true; }, }; }; const cancelablePromise = makeCancelable( new Promise(r => component.setState({...}})) ); cancelablePromise .promise .then(() => console.log(''resolved'')) .catch((reason) => console.log(''isCanceled'', reason.isCanceled)); cancelablePromise.cancel(); // Cancel the promise

Tomado de: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html


Si desea evitar que se ejecuten todas las capturas / capturas, puede hacerlo mediante la inyección de una promesa que nunca se resolverá. Probablemente tiene reprocusiones de pérdida de memoria, pero solucionará el problema y no debería causar demasiada memoria desperdiciada en la mayoría de las aplicaciones.

new Promise((resolve, reject) => { console.log(''first chain link executed'') resolve(''daniel''); }).then(name => { console.log(''second chain link executed'') if (name === ''daniel'') { // I don''t want to continue the chain, return a new promise // that never calls its resolve function return new Promise((resolve, reject) => { console.log(''unresolved promise executed'') }); } }).then(() => console.log(''last chain link executed'')) // VM492:2 first chain link executed // VM492:5 second chain link executed // VM492:8 unresolved promise executed


Si p es una variable que contiene una Promesa, entonces p.then(empty); debería descartar la promesa cuando finalmente se complete o si ya está completa (sí, sé que esta no es la pregunta original, pero es mi pregunta). "vacío" es la function empty() {} . Soy un principiante y probablemente esté equivocado, pero estas otras respuestas parecen demasiado complicadas. Se supone que las promesas son simples.


versión simple :

simplemente dé la función de rechazo.

class CancelablePromise { constructor(executor) { let _reject = null; const cancelablePromise = new Promise((resolve, reject) => { _reject = reject; return executor(resolve, reject); }); cancelablePromise.cancel = _reject; return cancelablePromise; } }

una solución wraper (fábrica)

La solución que encontré es pasar un objeto cancel_holder. Tendrá una función de cancelación. Si tiene una función de cancelación, entonces es cancelable.

Esta función de cancelación rechaza la promesa con Error (''cancelado'').

Antes de resolver, rechazar o on_cancel impiden que se llame a la función cancelar sin razón.

He encontrado conveniente pasar la acción de cancelación por inyección

const p = new CancelablePromise((resolve, reject) => { setTimeout(() => { console.log(''resolved!''); resolve(); }, 2000); }) p.catch(console.log); setTimeout(() => { p.cancel(new Error(''Fucked up!'')); }, 1000);


const makeCancelable = promise => { let rejectFn; const wrappedPromise = new Promise((resolve, reject) => { rejectFn = reject; Promise.resolve(promise) .then(resolve) .catch(reject); }); wrappedPromise.cancel = () => { rejectFn({ canceled: true }); }; return wrappedPromise; };

Uso:

const cancelablePromise = makeCancelable(myPromise); // ... cancelablePromise.cancel();