【问题标题】:Using Promises in a qooxdoo application在 qooxdoo 应用程序中使用 Promise
【发布时间】:2015-02-27 03:14:27
【问题描述】:

在一个 qooxdoo 类中,我有一组需要链接(序列化)的异步方法:

main: function() {
    async1();
},

async1: function() {
    var json = new qx.data.store.Json("http://...");
    json.addListener("loaded", function(event) {
        ...
        async2();
    });
},

async2: function() {
    // similar to async1
}

随着步骤数的增加,链条变得难以追踪,代码变得不可读。我们可以做的是使用Promises重写我们的代码:

main: function() {
    new Promise(this.async1)
    .then(function() {
        return new Promise(this.async2);
    }).then(function() {
        return new Promise(this.async3);
    }).catch(...);
},

async1: function(resolve, reject) {
    var json = new qx.data.store.Json("http://...");
    json.addListener("loaded", function(event) {
        ...
        resolve();
    });
},

async2: function(resolve, reject) {
    // similar to async1
}

这很有效,但只有在我们添加一些真正的逻辑之前。请记住,这是一个 qooxdoo 类,其中有很多东西封装在 this 中。但突然发现 then() 和 catch() 中使用的 async* 方法和匿名函数都将它们的 this 上下文绑定到了全局对象 window。为了能够使用实际的this,我们可以执行重新绑定:

main: function() {
    new Promise(this.async1.bind(this))
    .then(function() {
        this.doSomeStuff();
        return new Promise(this.async2.bind(this));
    }.bind(this)).then(function() {
        return new Promise(this.async3.bind(this));
    }.bind(this)).then(function() {
        this.doSomeFinalStuff();
    }.bind(this)).catch(function() {
        this.doStuffOnError();
    }.bind(this));
},

async1: function(resolve, reject) {
    this.doOtherStuff();
    var json = new qx.data.store.Json("http://...");
    json.addListener("loaded", function(event) {
        ...
        resolve();
    });
}

这终于可行了,但是,糟糕的是代码!有没有办法摆脱那些手动绑定?为什么当我们引用 this.async1 这个实例方法时,this 没有被隐式绑定?

【问题讨论】:

  • 只是一个观察:如果你需要调用一长串函数,无论它们是否异步,我认为你的整体设计很有可能有问题。 (当然,我可能错了 - 显然我没有看到代码。)至于默认情况下未绑定的函数:将其设为默认值将防止传递未绑定的函数 - 对于函数式编程风格至关重要 - 没有办法取消绑定函数。顺便说一句:如果您按照接受的答案中的建议使用self 变量,您是否需要上面的最后一个bind() 调用?
  • 当您需要多个服务器提供的资源来执行操作时,我已经看到需要这样连续的异步调用。我要做的是将所有逻辑打包在一个函数中,我将作为回调传递给需要异步获取的所有内容。此函数将首先检查是否满足所有先决条件,然后继续执行所有计算。 IMO,如果可能的话,切换到这样的代码结构将显着提高可读性——而不是将可能相关的逻辑散布在许多函数中,而是将它们全部放在一个地方。
  • 或者,您可以使用基于事件的结构 - 您可以使用回调触发事件,而不是传递回调,在这些事件上注册事件处理程序。这仍然会改进代码结构,IMO。有了简单的承诺,您就错过了使用描述性函数名称来记录意图的机会。对于事件,您可以获得两种可能的文档记录:事件名称和处理程序名称。
  • 试图想出链接异步调用的最佳位置——如果你实现了质询-响应身份验证机制,我认为使用异步调用链会更具描述性——我上面描述的两个解决方案没有提供强有力的、明确的因果顺序保证(尽管 qooxdoo 事件提供了隐含的),但它是必需的,并且在质询-响应身份验证的情况下记录它是很好的。但除了类似的情况,我认为链接异步调用对你的代码的读者来说并不好。

标签: javascript ecmascript-6 qooxdoo ecmascript-harmony


【解决方案1】:

查看this answer out

基本上,看起来您从根本上没有做错任何事情。这就是上下文应该如何工作的方式。您真正需要的是针对您的特定用例的一些语法糖。您可以自己制作这些糖扩展,也可以使用库来完成一些繁重的工作。

您还可以像 Danbopes 建议的那样重构您的代码并组织它,这样您就可以减少内联绑定。这可以让您更好地阅读您的流程。

【讨论】:

    【解决方案2】:

    如果你在顶部添加var self = this;,在所有函数中你可以通过调用self来引用对象本身:

    main: function() {
        var self = this;
        new Promise(self.async1)
        .then(function() {
            return new Promise(self.async2);
        }).then(function() {
            return new Promise(self.async3);
        }).catch(...);
    },
    

    【讨论】:

    • 不幸的是,这只会释放我们一半的绑定(用于匿名函数的绑定)。 async* 方法仍将有其this 上下文被反弹,否则它将默认为window
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多