Есть ли способ очистки .then
кода JavaScriptPromise
экземпляра ?
Я написал тестовую среду JavaScript поверх QUnit . Платформа запускает тесты синхронно, выполняя каждый из них в файле Promise
. (Извините за длину этого блока кода. Я прокомментировал его как можно лучше, так что он кажется менее утомительным.)
/* 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);
});
});
}
Если время теста истекло, мое обещание тайм-аута будет включено assert.fail()
в тест, так что тест будет помечен как неудачный, что все хорошо, но тест продолжает выполняться, потому что тестовое обещание (result
) все еще ожидает его разрешения.
Мне нужен хороший способ отменить тест. Я могу сделать это, создав поле в модуле фреймворка this.cancelTest
или что-то в этом роде и время от времени проверяя (например, в начале каждой then()
итерации) в тесте, нужно ли отменять. Однако в идеале я мог бы использовать $$(at).on("timeout", /* something here */)
для очистки оставшихся then()
s на моемresult
переменной, чтобы ни один из остальных тестов не запускался.
Есть ли что-то подобное?
Быстрое обновление
Я пробовал использовать Promise.race([result, at.promise])
. Это не сработало.
Обновление 2 + путаница
Чтобы разблокировать меня, я добавил несколько строк с mod.cancelTest
/ polling в идею теста. (Я также удалил триггер события.)
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);
});
Я установил точку останова в catch
операторе, и он попал. Меня смущает то, чтоthen()
оператор не вызывается. Идеи?
Обновление 3
Прикинул последнее. fn.call()
выдавал ошибку, которую я не уловил, поэтому тестовое обещание отклонялось, прежде чем at.promise.catch()
могло ее разрешить.
источник
Prex
библиотеки для отмены обещания.Ответы:
Нет. По крайней мере, не в ECMAScript 6. Обещания (и их
then
обработчики) по умолчанию нельзя отменить (к сожалению) . На es-Discusc (например, здесь ) есть небольшая дискуссия о том, как это сделать правильно, но какой бы подход ни выиграл, он не попадет в ES6.Текущая точка зрения заключается в том, что создание подклассов позволит создавать отменяемые обещания с использованием вашей собственной реализации (не уверен, насколько хорошо это будет работать). .
Пока языковой комитет не определит лучший способ (надеюсь, ES7?), Вы все еще можете использовать пользовательские реализации Promise, многие из которых имеют отмену.
Текущее обсуждение находится на https://github.com/domenic/cancelable-promise и проектах https://github.com/bergus/promise-cancellation .
источник
kill
ее игнорируете, в каком, возможно, странном состоянии, побочные эффекты оставили вашу среду (так что вы обычно просто выбрасываете и это, например, любые незаконченные результаты). Однако неблокирующие или асинхронные функции созданы для работы в интерактивных приложениях, где вы хотите иметь такой более тонкий контроль над выполнением текущих операций.Хотя в ES6 нет стандартного способа сделать это, есть библиотека Bluebird. .
Существует также рекомендуемый способ, описанный в документации по реакции. Это похоже на то, что у вас было во 2 и 3 обновлениях.
Взято из: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html.
источник
Я действительно удивлен, что никто не упоминает
Promise.race
в качестве кандидата на это:источник
cancel()
все равно приведет к вызову журнала. `` `const actualPromise = new Promise ((разрешить, отклонить) => {setTimeout (() => {console.log ('фактический вызов'); resolve ()}, 10000)}); ``then
s), а не как отменитьsetTimeout
(=>clearTimeout
) или синхронный код, где, если вы не поставите if после каждой строки (if (canceled) return
), этого нельзя достичь. (Не делайте этого)Использование:
источник
На самом деле невозможно остановить выполнение обещания, но вы можете перехватить отклонение и вызвать его из самого обещания.
Использование:
источник
Есть несколько библиотек npm для отменяемых обещаний.
p-cancelable https://github.com/sindresorhus/p-cancelable
отменяемое обещание https://github.com/alkemics/CancelablePromise
источник
Вот наша реализация https://github.com/permettez-moi-de-construire/cancellable-promise
Используется как
который :
catch
звонкаПриветствуются заявки и комментарии
источник
Обещание можно отменить с помощью
AbortController
.Пример:
HTML
Живой пример
источник
простая версия :
просто выдать функцию отклонения.
обертка (завод)
решение, которое я нашел, - передать объект cancel_holder. у него будет функция отмены. если у него есть функция отмены, то его можно отменить.
Эта функция отмены отклоняет обещание с ошибкой («отменено»).
Перед разрешением, отклонением или on_cancel предотвращает беспричинный вызов функции отмены.
Я нашел удобным передать действие отмены путем инъекции
источник
Попробуйте прерывание обещания : https://www.npmjs.com/package/promise-abortable
источник
Если ваш код помещен в класс, вы можете использовать для этого декоратор. Такой декоратор у вас есть в утилитах-декораторах (
npm install --save utils-decorators
). Он отменит предыдущий вызов декорированного метода, если до разрешения предыдущего вызова был сделан другой вызов этого конкретного метода.https://github.com/vlio20/utils-decorators#cancelprevious-method
источник
Если вы хотите остановить выполнение всех этих / уловок, вы можете сделать это, введя обещание, которое никогда не будет выполнено. Вероятно, это повторяет утечку памяти, но это решит проблему и не должно вызывать слишком много потраченной впустую памяти в большинстве приложений.
источник
Установите свойство «отменено» для обещания, чтобы сигнализировать
then()
иcatch()
выходить раньше. Это очень эффективно, особенно для Web Workers, у которых существующие микрозадачи поставлены в очередь в Promises отonmessage
обработчиков.источник
Ответ @Michael Yagudaev у меня работает.
Но исходный ответ не связывал обернутое обещание с .catch () для обработки обработки отклонения, вот мое улучшение над ответом @Michael Yagudaev:
источник
Если p - это переменная, содержащая обещание, то
p.then(empty);
следует отклонить обещание, когда оно в конечном итоге завершится или если оно уже завершено (да, я знаю, что это не исходный вопрос, но это мой вопрос). "пустой" естьfunction empty() {}
. Я просто новичок и, вероятно, ошибаюсь, но эти другие ответы кажутся слишком сложными. Обещания должны быть простыми.источник
Я все еще работаю над этой идеей, но вот как я реализовал отменяемое обещание, используя
setTimeout
в качестве примера.Идея состоит в том, что обещание разрешается или отклоняется всякий раз, когда вы решили, что это так, поэтому нужно решить, когда вы хотите отменить, удовлетворить критерию, а затем вызвать
reject()
функцию самостоятельно.Во-первых, я думаю, что есть две причины завершить обещание раньше: чтобы завершить его (что я назвал разрешением ) и отменить (что я назвал отклонением ). Конечно, это только мое ощущение. Конечно, есть
Promise.resolve()
метод, но он находится в самом конструкторе и возвращает фиктивное разрешенное обещание. Этотresolve()
метод экземпляра фактически разрешает созданный объект обещания.Во-вторых, вы можете с радостью добавить к вновь созданному объекту обещания все, что угодно, прежде чем вернуть его, поэтому я только что добавил
resolve()
иreject()
методы, чтобы сделать его самодостаточным.В-третьих, уловка состоит в том, чтобы иметь возможность получить доступ к исполнителю
resolve
иreject
функциям позже, поэтому я просто сохранил их в простом объекте внутри замыкания.Я думаю, что решение простое, и я не вижу в нем серьезных проблем.
источник