如何同步运行异步任务?

时间:2018-03-07 10:24:32

标签: node.js socket.io

我正在使用以下node.js堆栈开发一个应用程序:Express / Socket.IO + React。在React中我有DataTables,您可以搜索并在每次击键时动态更新数据! :)

我使用Socket.IO进行数据获取,因此在每次击键时,客户端套接字都会发出一些参数,服务器会调用回调来返回数据。这就像一个魅力,但不保证返回的数据的返回顺序与客户端发送的顺序相同。

模拟:所以当我输入'a'时,服务器会以相同的'a'响应每个字符。

我找到了node.js的异步模块,并尝试使用队列按照收到的顺序返回任务。为简单起见,我使用setTimeout延迟了第二个传入任务,以模拟执行速度慢的数据库查询:

声明:

const async = require('async');
var queue = async.queue(function(task, callback) {

if(task.count == 1) {
   setTimeout(function() {
     callback();
     }, 3000);
   } else {
     callback();
   }
}, 10);

用法:

socket.on('result', function(data, fn) {
    var filter = data.filter;

      if(filter.length === 1) { // TEST SYNCHRONOUSLY
          queue.push({name: filter, count: 1}, function(err) {
            fn(filter);
            // console.log('finished processing slow');
          });


      } else {
        // add some items to the queue
        queue.push({name: filter, count: filter.length}, function(err) {
          fn(data.filter);
          // console.log('finished processing fast');
        });
      }

    });

但是当我搜索 abc 时,我在客户端控制台中收到它的方式如下:

ab - > abc - > a(3秒后)

我希望它像这样返回:a(3秒后) - > ab - > ABC

我的想法是队列运行setTimeout然后更进一步,最后setTimeout稍后在事件循环的某处被触发。这导致稍后返回的搜索过滤器早于慢速执行过滤器。

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

首先提出一些意见,这可能有助于澄清您对异步调用的理解:

  1. 使用“timeout”尝试对齐异步调用是一个坏主意,这不是异步调用的想法。您永远不会知道异步调用需要多长时间,因此您永远无法设置适当的超时。

  2. 我相信您误解了您所描述的异步库中队列的使用情况。可以在此处找到队列的文档。

  3. 在此处复制粘贴文档,以便更改或关闭更改内容:

      

    使用指定的并发性创建队列对象。添加到队列的任务是并行处理的(直到并发限制)。如果所有工作人员都在进行中,则该任务将排队等候,直到有一个工作可用。一旦工人完成任务,就会调用该任务的回调。

    上述意味着队列可以简单地用于优先考虑给定工作人员可以执行的异步任务。不同的异步任务仍可在不同时间完成。

    潜在解决方案

    根据您的要求,您的问题有一些解决方案。

    1. 您一次只能发送一个异步呼叫并等待第一个异步呼叫完成,然后再发送下一个
    2. 存储结果,仅在所有通话完成后向用户显示结果
    3. 除了最新的异步调用
    4. 之外,您将忽略所有通话

      在您的情况下,我会选择解决方案3 ,因为您正在搜索某些内容。如果他们在获得“a”的回复之前已经在搜索“abc”,你为什么要关心“a”的结果?

      这可以通过为每个请求提供一个时间戳然后根据最新的时间戳进行排序来完成。

答案 1 :(得分:0)

<强> SOLUTION:

服务器:

exports = module.exports = function(io){
io.sockets.on('connection', function (socket) {

  socket.on('result', function(data, fn) {

    var filter = data.filter;
    var counter = data.counter;

      if(filter.length === 1 || filter.length === 5) { // TEST SYNCHRONOUSLY
          setTimeout(function() {
            fn({ filter: filter, counter: counter}); // return to client
          }, 3000);

      } else {
          fn({ filter: filter, counter: counter}); // return to client
      }

    });

  });


}

客户端:

export class FilterableDataTable extends Component {

constructor(props) {
    super();
    this.state = {
        endpoint: "http://localhost:3001",
        filters: {},
        counter: 0
    };

    this.onLazyLoad = this.onLazyLoad.bind(this);
}

onLazyLoad(event) {
    var offset = event.first;

    if(offset === null) {
        offset = 0;
    }

    var filter = ''; // filter is the search character

    if(event.filters.result2 != undefined) {
        filter = event.filters.result2.value;

    }

    var returnedData = null;

    this.state.counter++;

        this.socket.emit('result', { 
            offset: offset,
            limit: 20,
            filter: filter,
            counter: this.state.counter
        }, function(data) {
            returnedData = data;

         console.log(returnedData);

            if(returnedData.counter === this.state.counter) {

                console.log('DATA: ' + JSON.stringify(returnedData));
            } 
}
然而,这会将不需要的数据发送到客户端,而客户端则忽略它。有人想进一步优化这种沟通吗?例如,一种将旧数据保存在服务器上并仅发送最新数据的方法?