【问题标题】:Preserving asynchronous query order in Breeze/Angular在 Breeze/Angular 中保留异步查询顺序
【发布时间】:2014-05-02 20:47:57
【问题描述】:

我正在使用 BreezeJS 来处理 ng-grid 的服务器端过滤。为此,我只需 $watch'ing 更改 ng-grid 过滤器,并使用 BreezeJS 刷新我的数据。但是,如果我输入得足够快,AJAX 查询最终可能会出现乱序。

我正在考虑将 GUID 与每个查询相关联(通过闭包),并跟踪发出的最后一个查询请求的 GUID。然后忽略返回的与该 GUID 不匹配的任何查询(我不需要过时的查询结果 - 只需要最新的)。

但是有没有更好或更惯用的方法来处理这个问题,尤其是在 Angular/Breeze 中?

【问题讨论】:

    标签: javascript ajax angularjs breeze


    【解决方案1】:

    @Adam 针对您的主要问题提出了一个重要的想法……驯服您在用户在搜索框中输入时发出的一连串查询。

    去抖用户输入

    在开始取消请求之前,您应该“去抖动”(AKA,“节流”)搜索条目。这意味着在向服务器发出查询之前等待用户停止输入。

    在网上搜索“Angular”和“Debounce”,您会发现许多技术。我使用的一种方法是将搜索框绑定到ngChanged=vm.searchChanged()。然后 ViewModel 的 searchChanged 方法将启动 $timeout。如果用户在 1/2 秒内输入任何内容,我会取消该超时并开始另一个。经过 500 毫秒的沉默后,我将开始查询。如果他们按下回车键,我也会立即开始模糊处理。

    当 Angular v.1.3 发布时(现在很快),“去抖动”将成为 Angular 绑定的一部分。我期待着破坏我的自制去抖代码。

    取消

    假设用户停止输入 500 毫秒,查询开始,......并且不耐烦的用户想要取消请求。她不能在 Breeze v.1.4.11 中做到这一点。她将能够在 v.1.4.12 中。

    我刚刚为 jQuery 和 Angular 扩展了 Breeze AJAX 适配器,以方便取消和超时以响应 another question of yours

    响应顺序

    还有其他情况会启动多个请求。响应不一定按照您的请求顺序到达。这就是异步的本质。

    您绝对可以自己保持订单正常。请记住,您构建了回调。您可以维护一个应用程序范围的请求计数器,为每个 Query 和 Save ... 递增,然后在回调中引用。

    我在DocCode:queryTests.js 中写了一个说明性示例,展示了一种方法:

    /*********************************************************
    * Dealing with response order
    * It's difficult to make the server flip the response order
    * (run it enough times and the response order will flip)
    * but the logic of this test manifestly deals with it
    * because of the way it assigns results.
    *********************************************************/
    asyncTest("can sequence results that arrive out of order", 3, function() {
        var nextRequestId = 0;
        var em = newEm();
        var promises = [];
        var results = [];
        var arrived = [];
    
        promises.push(breeze.EntityQuery.from('Customers')
            .where('CompanyName', 'startsWith', 'a')
            .using(em).execute()
            .then(makeSuccessFn()).catch(handleFail));
    
        promises.push(breeze.EntityQuery.from('Customers')
            .where('CompanyName', 'startsWith', 's')
            .using(em).execute()
            .then(makeSuccessFn()).catch(handleFail));
    
        function makeSuccessFn() {
            var requestId = nextRequestId++;
            return function success(data) {
                // Don't know which response arrived first?
                // Sure you do. Just refer to the requestId which is a capture
                arrived.push(requestId);
                results[requestId] = data.results;
                assertWhenDone();
            }
        }
    
        function assertWhenDone() {
            if (results[0] && results[1]) {
                start(); // we're done
                // Here we report the actual response order
                ok(true, "Request #{0} arrived before #{1}".format(arrived[0], arrived[1]));
                // no matter what the response order
                // the 'a' companies go in results slot #0
                // the 's' companies go in results slot #1
                var aCompany = results[0][1].CompanyName();
                var sCompany = results[1][1].CompanyName();
                equal(aCompany[0].toLowerCase(), 'a',
                    "company from first batch should be an 'a', was " + aCompany);
                equal(sCompany[0].toLowerCase(), 's',
                    "company from second batch should be an 's', was " + sCompany);
            }
        }
    });
    

    2015 年 1 月 21 日更新

    我应该提到all promises 方法将响应数组传递给保留请求顺序的then(...) 成功回调。如果您碰巧在同一个地方同时发出多个查询并且可以一起等待它们(如上例所示),则您不需要requestId 的所有繁重工作。就这样吧……

    var promises = [];
    
    promises.push(breeze.EntityQuery.from('Customers')
            .where('CompanyName', 'startsWith', 'a')
            .using(em).execute();
    
    promises.push(breeze.EntityQuery.from('Customers')
            .where('CompanyName', 'startsWith', 's')
            .using(em).execute();
    
    // Q.all waits until all succeed or one of them fails
    // breeze.Q is typically $q in an Angular app
    breeze.Q.all(promises).then(allSucceeded).catch(atLeastOneFail);
    
    function allSucceeded(responses) {
       // response[0] is from the first 'a' query regardless of when it finished.
       // response[1] is from the second 's' query regardless of when it finished.      
    }
    

    【讨论】:

    • 这是一个了不起的答案!
    【解决方案2】:

    在用户键入时取消任何先前的请求,这样,只有对当前请求的响应,如果它完成,将更新结果。

    仅限伪代码:

    var req;
    
    function search(searchTerm) {
         req && req.cancel();
         req = queryServer().then(resultsReturned);
    }
    
    function resultsReturned() {
    }
    

    【讨论】:

    • 这肯定是要走的路,虽然我不确定 Breeze/Angular 是否可行
    猜你喜欢
    • 2012-06-29
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 2016-05-26
    相关资源
    最近更新 更多