在Breeze/Angular中保留异步查询顺序

Eri*_*ric 2 javascript ajax angularjs breeze

我正在使用BreezeJS来处理ng-grid的服务器端过滤.为此,我只是$watch要更改ng-grid过滤器,并使用BreezeJS刷新我的数据.如果我输入的速度足够快,那么AJAX查询最终可能会出现故障.

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

但有没有更好或更惯用的处理方式,特别是在Angular/Breeze中?

War*_*ard 6

@Adam对您的主要问题提出了一个重要的想法...当用户在搜索框中输入时,驯服您正在解雇的一系列查询.

去抖用户输入

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

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

当Angular v.1.3发布时(很快就会发布),"debounce"将成为Angular绑定的一部分.我期待废弃我自制的辩护代码.

取消

想象一下,用户停止键入500毫秒,查询开始,......并且不耐烦的用户想要取消请求.她不能在Breeze v.1.4.11中那样做.她将能够在第6.1.12节.

我只是为jQuery和Angular扩展了Breeze AJAX适配器,以便为你的另一个问题提供取消和超时功能.

响应顺序

在其他情况下,您可以启动多个请求.答复不一定按照您的请求顺序到达.这就是异步的本质.

你绝对可以自己保持秩序.请记住,您构造了回调.您可以维护一个应用程序范围的请求计数器,您可以为每个查询和保存添加...然后在回调中引用.

我在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);
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

2015年1月21日更新

我应该提到allpromises方法将响应数组传递给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.      
}
Run Code Online (Sandbox Code Playgroud)