【问题标题】:How to set callback order invocation same as target function invocation如何设置与目标函数调用相同的回调顺序调用
【发布时间】:2014-11-01 20:13:47
【问题描述】:

我的项目有问题。

为了描述这个问题,我写了简化的代码sn-p:

function waitFor(fnReady, fnCallback) {
    var check = function() {
        if (fnReady()) {
            fnCallback();
        }
        else {
            setTimeout(check, 100);  // wait another 100ms, and try again
        }
    };

    check();
}


var result = 0;
var flag = true;
function ajaxRequest() {
    setTimeout(
         function() { flag = false; 
                     console.log('ping');
                    },3000
    );
}

function ajaxRequestHandler() {
    setTimeout(
         function() { flag = true; 
                      console.log('pong');
                    }, 200
    );
}
for(var i =0;i<10; i++){   
    waitFor(function() { return flag; }, ajaxRequest);
    waitFor(function() { return !flag; }, ajaxRequestHandler);
}

它返回:

ping - 10 times
pong - 10 times

想要的结果:

ping 
3 second timeout 
ping
---------------------
ping
3 second timeout 
pong
--------------------
.....

您能帮我更正我的代码吗?

更新

实际问题:

我有一张谷歌地图。
我有很多地方需要重画。

如果我发送非常重要的应用程序逻辑

request1
request2
request3
request4

我应该按这个顺序处理响应

handle response of request1
handle response of request2
handle response of request3
handle response of request4 

我不知道请求顺序的问题。

在文件的不同位置,我看到以下代码行:

google.maps.event.addListener(searchBox, 'bounds_changed', renderTerminalsOnMapAndFitBounds);
...
$.getJSON('getAllTerminals.json', renderTerminalsOnMapAndFitBounds);
.....
$.getJSON('getAllTerminalsInsideRectangle.json', renderTerminalsOnMapAndFitBounds);
...
$.getJSON('getAllTerminalsInsideCircle.json', renderTerminalsOnMapAndFitBounds);
...
$.getJSON('getBigTerminals.json', renderTerminalsOnMapAndFitBounds);
........

renderTerminalsOnMapAndFitBounds 方法向服务器发送请求,并在地图上成功替代渲染结果。但是这个事件经常发生

【问题讨论】:

  • 这与您之前的问题有什么不同:stackoverflow.com/questions/26691226/…? JS 中的异步行为不应该通过轮询来完成。它应该通过利用已经提供的完成回调来完成。此外,这些问题可能会更好地成为具有真实代码的真实用例,而不是您将它们表述为理论讨论的方式。
  • 我的问题得到了正确答案,但我不能在我的实际应用程序中使用这种方式
  • 现在我正在尝试不要使用延迟。
  • 然后,向我们展示您遇到的实际问题,而不是一些理论讨论。只有这样,我们才能为您的实际问题提供最佳解决方案。我坚持我的意见,即轮询异步事件的完成不是编写异步代码的正确方法。如果您不想使用 Promise,请使用完成回调。
  • 不,我不清楚。请编辑您的问题以包含对此实际问题的更详细描述。只有这样,您才能充分利用这里的帮助。

标签: javascript jquery synchronization


【解决方案1】:

试试这个模式

var map = "abcdefghi".split("");
var responses = []; // collect responses
$.ajaxSetup({
    beforeSend : function(jqxhr, settings) {
      jqxhr.id = Number(settings.data.split(/id=/)[1]); // add `id` to `request`
        console.log(settings.data.split(/id=/)[1]);
    }
});
var request = function(id, data) {
    // append `id` to `id` data
    return $.post("/echo/json/", {json:JSON.stringify([data]), id:id})
};

$.each(map, function(k, v) {
    setTimeout(function() {
      request(k + 1, v)
      .done(function(data) {
        // do stuff at each response
        console.log(data); // note return values
      })
      .always(function(data, textStatus, jqxhr) {
          // do stuff at each response
          responses.push([jqxhr.id, data[0]]);
          // do stuff when all requests completed , results items in `responses`
          if (responses.length === map.length) {
              responses.sort(); // sort `responses` based on `id`
              // do stuff with `responses`
              console.log(responses);
          }
      });
    },1 + Math.random() * 1000) // async
});

jsfiddle http://jsfiddle.net/guest271314/g254bbjg/

【讨论】:

  • 对不起,我是 javascript 的新手,这段代码绝对不清楚。是否可以使您的代码更接近我的初始代码?
  • @gstackoverflow 当request 被创建时,id 被添加到request 中。在 jsfiddle 查看 console 时,收到未排序的响应。 id 和来自 request 的响应被推送到 responses 数组。 responses 数组是基于 id 排序的,这将导致 responses 以与 requests 相同的方式排序。然后应该能够以线性顺序处理responses;即 "handle response of request1 handle response of request2 handle response of request3 handle response of request4" ;因为responses应该对应requests的顺序。
  • 您将 id 添加到请求中。您如何理解响应对应请求?
  • 如果正确解释问题,请求按顺序调用“对于应用程序逻辑非常重要,如果我发送 request1 request2 request3 request4”?如果按上述顺序发送,id应该对应request发送的顺序;即 1 -> 2 -> 3 -> 4。添加了 setTimeout 以用于异步响应。请注意 jsfiddle 上 console 的回复。 Piece 尝试使用递增 id 然后在收到所有响应后排序,返回项目以请求顺序 1 -> 2 -> 3 -> 4。然后可以以相同的顺序处理响应处理程序 1 -> 2 -> 3 -> ,甚至如果响应 8 在响应 1 之前返回。
【解决方案2】:

我的变种:

var index = 0;
// callback function
function tryMe (param1) { 
    waitFor(function(){return param1 == index}, 
            function(){console.log(param1);
                       index++;
                      }
    )   
} 

// callback executer 
function callbackTester (callback,i) {     
    setTimeout( function(){callback(i);}, 20000 - i*1000); 
} 

// test function
for(var i=0 ; i<10 ; i++){
    callbackTester ( tryMe,i );
}

function waitFor(fnReady, fnCallback) {
    var check = function() {
        if (fnReady()) {
            fnCallback();
        }
        else {
            setTimeout(check, 100);  // wait another 100ms, and try again
        }
    };

    check();
}

http://jsfiddle.net/x061dx75/17/

【讨论】:

    【解决方案3】:

    我个人会为此使用 Promise,但你说没有 Promise(不知道为什么),所以这是一个普通 javascript 中的通用序列器算法(在下面链接的 jsFiddle 中测试):

    function sequence(fn) {
        // initialize sequence data upon first use
        if (typeof sequence.low === "undefined") {
            sequence.low = sequence.high = 0;
            sequence.results = {};
        }
        // save id in local variable so we can reference it in the closure from the function below
        var id = sequence.high;
    
        // advance to next sequence number
        ++sequence.high;
    
        // initialize the result value for this sequence callback
        sequence.results[id] = {fn: fn, args: [], ready: false, context: null};
    
        return function(/* args */) {
            // save args and context and mark it ready
            var args = Array.prototype.slice.call(arguments, 0);
            // get the results object for this callback and save info in it
            var thisResult = sequence.results[id];
            thisResult.args = args;
            thisResult.context = this;
            thisResult.ready = true;
    
            // now process any requests in order that are ready
            for (var i = sequence.low; i < sequence.high; i++) {
                var result = sequence.results[i];
                // if this one is ready, process it
                if (result.ready) {
                    // increment counter past this result
                    ++sequence.low;
                    // remove this stored result
                    delete sequence.results[i];
                    // process this result
                    result.fn.apply(result.context, result.args);
                } else {
                    // if this one not ready, then nothing to do yet
                    break;
                }
            }
        };
    }
    
    // your usage:
    
    google.maps.event.addListener(searchBox, 'bounds_changed', sequence(renderTerminalsOnMapAndFitBounds));
    ...
    $.getJSON('getAllTerminals.json', sequence(renderTerminalsOnMapAndFitBounds));
    .....
    $.getJSON('getAllTerminalsInsideRectangle.json', sequence(renderTerminalsOnMapAndFitBounds));
    ...
    $.getJSON('getAllTerminalsInsideCircle.json', sequence(renderTerminalsOnMapAndFitBounds));
    ...
    $.getJSON('getBigTerminals.json', sequence(renderTerminalsOnMapAndFitBounds));
    ........
    

    工作演示:http://jsfiddle.net/jfriend00/aqugm1fs/


    从概念上讲,它的作用如下:

    1. 传递一个替代完成处理程序来代替正常的完成回调。
    2. 此替代函数使用序列 ID 标记每个响应并保存原始完成处理程序。
    3. 如果一个响应返回,而另一个具有较低序列 ID 的响应仍处于待处理状态,则结果将被存储并保存以备后用。
    4. 当每个响应进入时,它会按顺序处理尽可能多的响应

    注意:虽然您的所有示例都使用相同的回调函数,但它适用于任何回调函数,因此它可以与不同类型的操作混合使用。

    【讨论】:

    • 修复了在第一个序列完成后用于第二个事件序列时的错误。
    • @gstackoverflow - 你去哪儿了?我对结果进行了编码和测试,以完全按照您的要求进行操作,现在您已经离开了。有点让我们想知道为什么我们费心去帮忙。
    猜你喜欢
    • 1970-01-01
    • 2013-05-09
    • 2012-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多