取消一个普通的 ECMAScript 6 Promise 链 [英] Cancel a vanilla ECMAScript 6 Promise chain

查看:17
本文介绍了取消一个普通的 ECMAScript 6 Promise 链的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有清除 JavaScript Promise 实例的 .then 的方法?

Is there a method for clearing the .thens of a JavaScript Promise instance?

我在 QUnit 之上编写了一个 JavaScript 测试框架.该框架通过在 Promise 中运行每个测试来同步运行测试.(抱歉这段代码太长了.我尽量评论了,所以感觉不那么乏味了.)

I've written a JavaScript test framework on top of QUnit. The framework runs tests synchronously by running each one in a Promise. (Sorry for the length of this code block. I commented it as best I can, so it feels less tedious.)

/* 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 + "
" + 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() 在测试上,以便将测试标记为失败,这一切都很好,但测试继续运行因为测试 Promise (result) 仍在等待解决它.

If a test times out, my timeout Promise will assert.fail() on the test so that the test is marked as failed, which is all well and good, but the test continues to run because the test Promise (result) is still waiting to resolve it.

我需要一个好的方法来取消我的测试.我可以通过在框架模块 this.cancelTest 或其他东西上创建一个字段来做到这一点,并每隔一段时间检查一次(例如在每个 then() 迭代的开始)测试是否取消.但是,理想情况下,我可以使用 $$(at).on("timeout",/* something here */) 来清除剩余的 then()我的 result 变量,因此测试的其余部分都不会运行.

I need a good way to cancel my test. I can do it by creating a field on the framework module this.cancelTest or something, and checking every so often (e.g. at the beginning of each then() iteration) within the test whether to cancel out. However, ideally, I could use $$(at).on("timeout", /* something here */) to clear the remaining then()s on my result variable, so that none of the rest of the test is run.

这样的东西存在吗?

我尝试使用 Promise.race([result, at.promise]).它没有用.

I tried using Promise.race([result, at.promise]). It didn't work.

为了解除对我的阻止,我在测试想法中添加了几行 mod.cancelTest/polling.(我还删除了事件触发器.)

To unblock me, I added a few lines with the mod.cancelTest/polling within the test idea. (I also removed the event trigger.)

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() 语句.想法?

I set a breakpoint in the catch statement, and it's being hit. What's confusing me now is that the then() statement isn't being called. Ideas?

想出了最后一件事.fn.call() 抛出了一个我没有发现的错误,所以在 at.promise.catch() 可以解决它之前,测试承诺被拒绝了.>

Figured the last thing out. fn.call() was throwing an error which I didn't catch, so the test promise was rejecting before at.promise.catch() could resolve it.

推荐答案

是否有清除 JavaScript Promise 实例的 .then 的方法?

没有.至少在 ECMAScript 6 中没有.默认情况下,Promise(以及它们的 then 处理程序)是不可取消的(不幸的是).在 es-discuss(例如 此处)上有一些关于如何执行此操作的讨论以正确的方式,但无论哪种方法会获胜,它都不会在 ES6 中落地.

No. Not in ECMAScript 6 at least. Promises (and their then handlers) are uncancellable by default (unfortunately). There is a bit of discussion on es-discuss (e.g. here) about how to do this in the right way, but whatever approach will win it won't land in ES6.

目前的观点是,子类化将允许使用您自己的实现创建可取消的承诺(不确定它的效果如何).

The current standpoint is that subclassing will allow to create cancellable promises using your own implementation (not sure how well that'll work).

直到语言委员会找到最佳方法(希望是 ES7?),您仍然可以使用用户态 Promise 实现,其中许多功能取消.

Until the language commitee has figured out the best way (ES7 hopefully?) you can still use userland Promise implementations, many of which feature cancellation.

当前讨论在 https://github.com/domenic/cancelable-promisehttps://github.com/bergus/promise-cancellation 草稿.

Current discussion is in the https://github.com/domenic/cancelable-promise and https://github.com/bergus/promise-cancellation drafts.

这篇关于取消一个普通的 ECMAScript 6 Promise 链的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆