Existe um método para limpar os .then
s de uma Promise
instância de JavaScript ?
Eu escrevi uma estrutura de teste de JavaScript em cima do QUnit . A estrutura executa testes de forma síncrona, executando cada um em a Promise
. (Desculpe pela extensão deste bloco de código. Comentei-o da melhor maneira que pude, por isso parece 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);
});
});
}
Se um teste atingir o tempo limite, minha promessa de tempo limite será assert.fail()
no teste para que o teste seja marcado como com falha, o que é muito bom, mas o teste continua a ser executado porque a promessa de teste ( result
) ainda está esperando para resolvê-lo.
Preciso de uma boa maneira de cancelar meu teste. Posso fazer isso criando um campo no módulo do framework this.cancelTest
ou algo assim, e verificando de vez em quando (por exemplo, no início de cada then()
iteração) dentro do teste para cancelar. No entanto, idealmente, eu poderia usar $$(at).on("timeout", /* something here */)
para limpar os then()
s restantes em minha result
variável, de modo que nada do resto do teste seja executado.
Será que algo assim existe?
Rápida atualização
Tentei usar Promise.race([result, at.promise])
. Não funcionou.
Atualização 2 + confusão
Para me desbloquear, adicionei algumas linhas com mod.cancelTest
/ polling na ideia de teste. (Eu também removi o gatilho de 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);
});
Eu defini um ponto de interrupção na catch
declaração e ele está sendo atingido. O que está me confundindo agora é que a then()
declaração não está sendo chamada. Ideias?
Atualização 3
Descobri a última coisa. fn.call()
estava gerando um erro que não percebi, então a promessa de teste foi rejeitada antes que at.promise.catch()
pudesse resolvê-lo.
fonte
Prex
biblioteca para cancelamento de promessa.Respostas:
Não. Não no ECMAScript 6, pelo menos. As promessas (e seus
then
manipuladores) não podem ser canceladas por padrão (infelizmente) . Há um pouco de discussão em es-discuss (por exemplo, aqui ) sobre como fazer isso da maneira certa, mas seja qual for a abordagem que vai vencer, não vai cair no ES6.O ponto de vista atual é que a subclasse permitirá criar promessas canceláveis usando sua própria implementação (não tenho certeza se isso funcionará bem) .
Até que o comitê de linguagem tenha descoberto a melhor maneira (esperançosamente ES7?), Você ainda pode usar as implementações do Promise do userland, muitas das quais com cancelamento de recurso.
Discussão atual está na https://github.com/domenic/cancelable-promise e https://github.com/bergus/promise-cancellation rascunhos.
fonte
kill
o ignora em que estado possivelmente estranho os efeitos colaterais deixaram seu ambiente (então você normalmente apenas joga fora isso, por exemplo, quaisquer saídas incompletas). Funções não bloqueadoras ou assíncronas, no entanto, são criadas para funcionar em aplicativos interativos, onde você deseja ter esse tipo de controle mais preciso sobre a execução de operações em andamento.Embora não haja uma maneira padrão de fazer isso no ES6, existe uma biblioteca chamada Bluebird para lidar com isso.
Há também uma forma recomendada descrita como parte da documentação de reação. É semelhante ao que você tem nas suas 2 e 3 atualizações.
Retirado de: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
fonte
Estou realmente surpreso que ninguém menciona
Promise.race
como candidato para isto:fonte
cancel()
ainda resultará na chamada do log. `` `const actualPromise = new Promise ((resolver, rejeitar) => {setTimeout (() => {console.log ('real chamado'); resolve ()}, 10000)}); `` `then
s encadeados a serem executados), não como cancelarsetTimeout
(=>clearTimeout
) ou código síncrono, onde a menos que você coloque um if após cada linha (if (canceled) return
), isso não pode ser alcançado. (Não faça isso)Uso:
fonte
Na verdade, é impossível impedir a execução da promessa, mas você pode sequestrar a rejeição e chamá-la da própria promessa.
Uso:
fonte
Existem algumas bibliotecas npm para promessas canceláveis.
p-cancelável https://github.com/sindresorhus/p-cancelable
promessa cancelável https://github.com/alkemics/CancelablePromise
fonte
Aqui está nossa implementação https://github.com/permettez-moi-de-construire/cancellable-promise
Usado como
qual :
catch
chamadaPuxões e comentários são bem-vindos
fonte
A promessa pode ser cancelada com a ajuda de
AbortController
.Exemplo:
Html
Exemplo ao Vivo
fonte
versão simples :
apenas forneça a função de rejeição.
uma solução wraper (fábrica)
a solução que encontrei é passar um objeto cancel_holder. ele terá uma função de cancelamento. se tiver uma função de cancelamento, é cancelável.
Esta função de cancelamento rejeita a promessa com Erro ('cancelada').
Antes de resolver, rejeitar ou on_cancel evita que a função cancel seja chamada sem motivo.
Achei conveniente passar a ação de cancelamento por injeção
fonte
Tente abortável de promessa : https://www.npmjs.com/package/promise-abortable
fonte
Se o seu código for colocado em uma classe, você pode usar um decorador para isso. Você tem esse decorador em utils-decorators (
npm install --save utils-decorators
). Ele cancelará a chamada anterior do método decorado se antes da resolução da chamada anterior tiver sido feita outra chamada para aquele método específico.https://github.com/vlio20/utils-decorators#cancelprevious-method
fonte
Se você quiser impedir que todos os thens / catch sejam executados, você pode fazer isso injetando uma promessa que nunca será resolvida. Provavelmente ele tem reorientações de vazamento de memória, mas corrigirá o problema e não deve causar muito desperdício de memória na maioria dos aplicativos.
fonte
Defina uma propriedade "cancelada" na promessa de sinalizar
then()
ecatch()
sair mais cedo. É muito eficaz, especialmente em Web Workers que têm microtarefas existentes enfileiradas em Promises deonmessage
manipuladores.fonte
A resposta de @Michael Yagudaev funciona para mim.
Mas a resposta original não encadeava a promessa embrulhada com .catch () para lidar com o tratamento de rejeições, aqui está meu aprimoramento em cima da resposta de @Michael Yagudaev:
fonte
Se p for uma variável que contém uma promessa, então
p.then(empty);
deve descartar a promessa quando ela finalmente for concluída ou se já estiver completa (sim, eu sei que esta não é a pergunta original, mas é minha pergunta). "vazio" éfunction empty() {}
. Sou apenas um iniciante e provavelmente estou errado, mas essas outras respostas parecem muito complicadas. As promessas devem ser simples.fonte
Ainda estou trabalhando com essa ideia, mas aqui está como implementei uma promessa cancelável usando
setTimeout
como exemplo.A ideia é que uma promessa seja resolvida ou rejeitada sempre que você decidir que é, portanto, deve ser uma questão de decidir quando você deseja cancelar, satisfazer o critério e, em seguida, chamar a
reject()
função você mesmo.Em primeiro lugar, acho que há duas razões para terminar uma promessa mais cedo: terminá-la (o que chamei de resolver ) e cancelar (que chamei de rejeitar ). Claro, isso é apenas meu sentimento. Claro que existe um
Promise.resolve()
método, mas está no próprio construtor e retorna uma promessa resolvida fictícia. Esteresolve()
método de instância realmente resolve um objeto de promessa instanciado.Em segundo lugar, você pode facilmente adicionar qualquer coisa que você gosta de um objeto promessa recém-criado antes de devolvê-lo, e por isso tenho apenas acrescentado
resolve()
ereject()
métodos para torná-lo auto-suficiente.Terceiro, o truque é ser capaz de acessar o executor
resolve
e asreject
funções mais tarde, então simplesmente armazenei-os em um objeto simples de dentro do encerramento.Acho que a solução é simples e não consigo ver nenhum grande problema nela.
fonte