4

概括:

我们在使用 Ember 应用程序时遇到了以下情况,并且正在努力寻找合适的解决方案。

我们正在处理大量数据,因此我们对数据的一些请求相当慢。

最初,我们使用 Em.RSVP.hash 将我们的请求“捆绑”在每个路由的模型挂钩中。然而,这会锁定 UI,最终是不可接受的。

我们的解决方案是在 setupController 钩子中级联请求,如下所示:

setupController: (controller, model)->
    slowRequest1WhichReturnsAPromise().then (data)->
        # Do something with request 1 data

        slowRequest2WhichReturnsAPromise().then (data)->
            # Do something with request 2 data

            slowRequest3WhichReturnsAPromise().then (data)->
                # Do something with request 3 data

这行得通。我们获得即时页面加载,并且可以首先显示最相关的数据。

我们试图让 Ember 保持最新(在撰写本文时为 1.11.3)。我们没有使用 Ember 数据。

问题:

问题是用户界面已解锁。例如,用户可以注销(并被重定向到登录页面)。请求持续存在并最终完成,弄乱了我们在登录和注销时设置的 csrf 令牌。下次用户尝试登录时,他们会收到 422 错误。

一个更简单的场景是在长时间运行的请求完成之前更改过滤器设置。新请求(通常通过过滤器更改缩小范围)在初始请求之前完成。然后数据被最终完成的原始请求覆盖。

我们的直接解决方案是尝试杀死未决的承诺,但找不到“现代”的方式来做到这一点。

这有效:

allPromises: []

setupController: (controller, model)->
    deferredRequest1 = new Em.RSVP.defer()

    # Store all promises
    @get('allPromises').pushObject(deferredRequest1)

    # Fire off request 1
    # Spark up the next stage when request 1 finishes
    deferredRequest1.promise.then ->
        deferredRequest2 = new Em.RSVP.defer()
        @get('allPromises').pushObject(deferredRequest2)

# ... 
actions:
    signOut: ->
        @get('allPromises').forEach (promise)->
            promise.reject('canceled')

上面的内容有点难看和简化,以了解重点。

Ember Docs 声明“新代码应该使用 RSVP.Promise 构造函数而不是(延迟)”。但是,似乎没有办法使用新方法来做到这一点。

问题:

有没有一种现代的方式来取消未决的承诺/请求?
有没有更好的方法来拆分我们的页面加载以启用一种干净的方式来取消挂起的请求?

更新

我最终使用了延迟。它并不漂亮,但在我们重构 UI 并重新考虑对慢查询的需求之前,它会起作用。

谢谢你。

4

1 回答 1

0

首先,在您的“工作”解决方案中,您不会在注销期间取消慢查询。您只是拒绝终止承诺链的承诺,因此不会发送更多请求。(当 signOut 之前的最后一个查询返回时,它仍然可以改变你的 cookie。)

如果您已经可以接受,那么您需要做的是在承诺链中插入signOut-aware 检查,以便在用户退出时立即终止。

didSignOut: false

setupController: (controller, model) ->
    slowRequest1WhichReturnsAPromise().then ->
        return null if didSignOut
        slowRequest2WhichReturnsAPromise().then ->
            return null if didSignOut
            slowRequest3WhichReturnsAPromise().then ->
                return null if didSignOut
                // ...

动作:signOut: -> @set('didSignOut', true)

return null可以替换为您接下来要执行的任何操作。如果你想要一种集中的方式来处理这个事件,你仍然可以通过第二个参数拒绝承诺并为其.then()设置一个RSVP.on("error", ...)处理程序。

于 2015-06-23T09:58:30.270 回答