是否有清除.thenJavaScript Promise实例的方法?
.then
Promise
我已经在QUnit之上编写了一个JavaScript测试框架。该框架通过在.NET中运行每个测试来同步运行测试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); }); }); }
如果测试超时,我的超时Promise将会assert.fail()在测试中进行测试,以便将测试标记为失败,这一切都很好,但是测试继续运行,因为测试Promise(result)仍在等待解决它。
assert.fail()
result
我需要取消考试的好方法。我可以通过在框架模块之类的地方创建一个字段this.cancelTest,然后在测试中每隔一段时间(例如在每次then()迭代的开始)检查是否取消来做到这一点。但是,理想情况下,我可以$$(at).on("timeout", /* something here */)用来清除变量中的剩余then()s result,以便不运行其余所有测试。
this.cancelTest
then()
$$(at).on("timeout", /* something here */)
是否存在这样的东西?
我尝试使用Promise.race([result, at.promise])。没用
Promise.race([result, at.promise])
为了解除对我的限制,我mod.cancelTest在测试思路中在/ polling上添加了几行。(我还删除了事件触发器。)
mod.cancelTest
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()声明没有被调用。有想法吗?
catch
想通了最后一件事。 fn.call()抛出了一个我没有捕获的错误,因此测试承诺在at.promise.catch()解决之前就被拒绝了。
fn.call()
at.promise.catch()
不。至少在ECMAScript 6中没有。then默认情况下,不幸的是,承诺(及其处理程序)是无法取消的。关于es- discuss有一些有关如何正确执行此操作的讨论,但是无论哪种方法获胜,都不会在ES6中实现。
then
当前的观点是,子类化将允许使用您自己的实现来创建可取消的Promise(不确定其效果如何) 。
在语言委托人找到最佳方法之前 (希望是ES7?), 您仍然可以使用userland Promise实现,其中许多功能都取消了。