【问题标题】:Javascript function to handle concurrent async function more efficientlyJavascript函数更有效地处理并发异步函数
【发布时间】:2016-07-18 05:51:11
【问题描述】:

考虑以下几点:

  • 每秒最多可有 100 个并发请求的 Web 应用程序
  • 当前每个传入请求都会向端点发出 http 请求以获取一些数据(最多可能需要 5 秒)
  • 我只想发出一次 http 请求,即我不想同时调用同一个端点,因为它会返回相同的数据
  • 这个想法是只有第一个请求才会进行http调用来获取数据
  • 虽然此调用是“进行中”,但后续请求不会进行相同的调用,而是“等待”第一个进行中的请求完成。
  • 当对数据的初始http请求得到响应时,它必须响应所有带有数据的调用。
  • 我正在使用 Bluebird Promise 来实现执行 http 请求的异步函数。

我想创建/使用某种包装业务逻辑承诺方法的通用方法/类。这个通用方法/调用将知道何时调用实际的业务逻辑函数,何时等待飞行完成,然后在它有响应时解决所有等待的调用。

我希望已经有一个节点模块可以做到这一点,但想不出这种类型的实用程序会被称为什么。

类似于 lodash 油门/去抖动的东西,但不完全相同。

如果它不存在,我可以自己写,但很难为此想出一个合理的名称。

任何帮助将不胜感激。

【问题讨论】:

  • 一个 Web 应用程序只能有 6 个并发请求,因为这是浏览器的硬性限制。老IE只能做2个。

标签: javascript node.js promise bluebird


【解决方案1】:

你可以同时使用promise来防止重复请求

用nodejs编写的例子,你也可以在浏览器中使用这个模式

const rp = require('request-promise'), 
var wait = null; 

function getUser(req, rep, next){
  function userSuccess(){
    wait = null;
  };
  function userErr(){
    wait = null;
  };

  if (wait){
    console.log('a wait');
  }
  else{
    wait = rp.get({ url: config.API_FLIX + "/menu"});
  }

  wait.then(userSuccess).catch(userErr);
}

【讨论】:

    【解决方案2】:

    你可以实现一个 PromiseCaching,比如:

    module.exports = function request(url) {
        if (caches[url]) return caches[url];
        var promise = req(url);
    
        return (caches[url] = promise);
    };
    
    var req = require('');
    var caches = {};
    

    编辑

    让我解释一下:

    这里不是关于缓存响应,而是关于缓存承诺。 Nodejs 是单线程的,这意味着没有并发的函数调用,即使一切都是异步的,在某个时间点,只运行一段代码。这意味着,将有人首先使用 url y.com/foo 调用该函数,缓存中不会有任何承诺,因此它将触发 GET 请求,并将缓存并返回该承诺。当有人立即使用相同的 url 调用该函数时,不会触发更多请求,而是会返回此 url 的第一个承诺,并且消费者可以订阅 done/fail 回调。 当响应准备好并且promise被履行,并且有人使用相同的url发出请求时,它会再次获取缓存的promise,它已经准备好了。

    承诺缓存是防止重复异步任务的好方法。

    【讨论】:

    • 感谢您的回复,但我的要求可能不是很清楚。我不在这里使用缓存解决方案,因为我已经缓存了响应。
    • 此功能也将在服务器端 express 应用程序上。考虑当缓存为空或过期并且您同时收到多个请求时,每个请求都会发出这个 http 数据 GET 请求。这是我试图避免的。只有第一个请求应该发出服务器端 http 数据 GET 请求,任何后续请求都不应该而是“等待”第一个调用以响应并重新使用它。
    • 我现在明白了。很有魅力 - 我基本上使用节点缓存来缓存数据,现在我改变了它,所以它会缓存承诺。必须关闭节点缓存中的“useClones”才能使缓存的承诺起作用。
    【解决方案3】:

    Web 应用程序只能有 6 个并发请求,因为这是浏览器的硬性限制。旧版 IE 只能做 2 个。所以无论你做什么 - 这是一个硬性限制。

    一般情况下,你应该在服务端解决复用。

    现在 - 对于您的实际问题 - 您要求的那种缓存对于 Promise 来说非常简单。

    function once(fn) {
        var val = null; // cache starts as empty
        return () => val || (val = fn()); // return value or set it.
    }
    
    
    var get = once(getData);
    get();
    get(); // same call, even though the value didn't change.
    

    现在,您可能想要添加过期策略:

    function once(fn, timeout) {
      var val = null, timer = null; // cache starts as empty
      return () => val || (val = fn().tap(invalidate)); // return value and invalidate
      function invalidate() { 
        clearTimeout(timer); // remove timer.
        timer = setTimeout(() => val = null, timeout);
      }
    }
    
    var get = once(getData, 10000); 
    

    如果结果失败,您可能还想取消缓存:

    function once(fn, timeout) {
      var val = null, timer = null; // cache starts as empty
      return () => val || 
       (val = fn().catch(e => value = null, Promise.reject(e)).tap(invalidate));
      function invalidate() { 
        clearTimeout(timer); // remove timer.
        timer = setTimeout(() => val = null, timeout);
      }
    }
    

    由于原始功能是一行代码,因此没有帮助程序。

    【讨论】:

      猜你喜欢
      • 2022-06-18
      • 2018-12-12
      • 1970-01-01
      • 1970-01-01
      • 2012-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-31
      相关资源
      最近更新 更多