简体   繁体   English

Node.js异步,但仅处理第一个肯定的/定义的结果

[英]Node.js async, but only handle first positive/defined result

What is the best way to create parallel asynchronous HTTP requests and take the first result that comes back positive? 创建并行异步HTTP请求并获得返回正数的第一个结果的最佳方法是什么? I am familiar with the async library for JavaScript and would happy to use that but am not sure if it has exactly what I want. 我熟悉JavaScript的异步库,很乐意使用它,但不确定它是否具有我想要的。

Background - I have a Redis store that serves as state for a server. 背景-我有一个Redis存储,用作服务器的状态。 There is an API we can call to get some data that takes much longer than reaching the Redis store. 我们可以调用一个API来获取一些数据,这些数据花费的时间比到达Redis存储要长得多。

In most cases the data will already be in the Redis store, but in some cases it won't be there yet and we need to retrieve it from the API. 在大多数情况下,数据已经在Redis存储中,但在某些情况下尚不存在,因此我们需要从API检索数据。

The simple thing to do would be to query Redis, and if the value is not in Redis then go to the API afterwards. 最简单的方法是查询Redis,如果该值不在Redis中,则随后转到API。 However, we'll needlessly lose 20-50ms if the data is not yet in our Redis cache and we have to go to the API after failing to find the data with Redis. 但是,如果数据尚未存储在我们的Redis缓存中,我们将不必要地丢失20-50ms,并且在Redis无法找到数据后,我们必须转到API。 Since this particular API server is not under great load, it won't really hurt to go to the API simultaneously/in parallel, even if we don't absolutely need the returned value. 由于此特定的API服务器负载不大,因此即使我们绝对不需要返回的值,同时/并行访问该API也不会造成太大伤害。

//pseudocode below //下面的伪代码

async.minimum([

function apiRequest(cb){
   request(opts,function(err,response,body){
         cb(err,body.result.hit);
    }
},
function redisRequest(cb){
   client.get("some_key", function(err, reply) {
          cb(err,reply.result.hit);
     });
}], 

  function minimumCompleted(err,result){

   // this mimimumCompleted final callback function will be only fired once, 
   // and would be fired by one of the above functions - 
   // whichever one *first* returned a defined value for result.hit


 });

is there a way to get what I am looking for with the async library or perhaps promises, or should I implement something myself? 有没有办法通过异步库或promise获得我要的东西,还是我应该自己实现一些东西?

Use Promise.any([ap, bp]) . 使用Promise.any([ap, bp])

The following is a possible way to do it without promises. 以下是不做承诺的一种可能的方式。 It is untested but should meet the requirements. 它未经测试,但应符合要求。

To meet requirement of returning the first success and not just the first completion, I keep a count of the number of completions expected so that if an error occurs it can be ignored it unless it is the last error. 为了满足返回第一个成功而不只是第一个完成的要求,我对预期的完成数量进行计数,以便在发生错误时将其忽略,除非它是最后一个错误。

function asyncMinimum(a, cb) {
    var triggered = false;
    var completions = a.length;
    function callback(err, data) {
      completions--;
      if (err && completions !== 0) return;
      if (triggered) return;
      triggered = true;
      return cb(err, data);
    }
    a.map(function (f) { return f(callback); });
}


asyncMinimum([

function apiRequest(cb){
   request(opts,function(err,response,body){
         cb(err,body.result.hit);
    }
},
function redisRequest(cb){
   client.get("some_key", function(err, reply) {
          cb(err,reply.result.hit);
     });
}], 

  function minimumCompleted(err,result){

   // this mimimumCompleted final callback function will be only fired once, 
   // and would be fired by one of the above functions - 
   // whichever one had a value for body.result.hit that was defined

 });

The async.js library (and even promises) keep track of the number of asynchronous operations pending by using a counter. async.js库(甚至是Promise)通过使用计数器来跟踪待处理的异步操作的数量。 You can see a simple implementation of the idea in an answer to this related question: Coordinating parallel execution in node.js 您可以在以下相关问题的答案中看到该想法的简单实现: 在node.js中协调并行执行

We can use the same concept to implement the minimum function you want. 我们可以使用相同的概念来实现所需的minimum功能。 Only, instead of waiting for the counter to count all responses before triggering a final callback, we deliberately trigger the final callback on the first response and ignore all other responses: 只是,我们没有在触发最终回调之前等待计数器对所有响应进行计数,而是故意在第一个响应上触发最终回调,并忽略所有其他响应:

// IMHO, "first" is a better name than "minimum":

function first (async_functions, callback) {
    var called_back = false;

    var cb = function () {
        if (!called_back) {
            called_back = true; // block all other responses

            callback.apply(null,arguments)
        }
    }

    for (var i=0;i<async_functions.length;i++) {
        async_functions[i](cb);
    }
}

Using it would be as simple as: 使用它会很简单:

first([apiRequest,redisRequest],function(err,result){
    // ...
});

Here's an approach using promises. 这是一种使用诺言的方法。 It takes a little extra custom code because of the non-standard result you're looking for. 由于您要查找非标准结果,因此需要一些额外的自定义代码。 You aren't just looking for the first one to not return an error, but you're looking for the first one that has a specific type of result so that takes a custom result checker function. 您不仅在寻找第一个不返回错误的方法,而且还在寻找具有特定结果类型的第一个方法,以便采用自定义结果检查器功能。 And, if none get a result, then we need to communicate that back to the caller by rejecting the promise too. 而且,如果没有结果,那么我们也需要通过拒绝诺言将其传达给呼叫者。 Here's the code: 这是代码:

function firstHit() {
    return new Promise(function(resolve, reject) {
        var missCntr = 0, missQty = 2;

        function checkResult(err, val) {
            if (err || !val) {
                // see if all requests failed
                ++missCntr;
                if (missCntr === missQty) {
                    reject();
                }
            } else {
                resolve(val);
            }
        }
        request(opts,function(err, response, body){
            checkResult(err, body.result.hit);
        }

        client.get("some_key", function(err, reply) {
            checkResult(err, reply.result.hit);
        });
    });
}

firstHit().then(function(hit) {
    // one of them succeeded here
}, function() {
    // neither succeeded here
});

The first promise to call resolve() will trigger the .then() handler. 调用resolve()的第一个承诺将触发.then()处理程序。 If both fail to get a hit, then it will reject the promise. 如果两者都未能取得成功,那么它将拒绝承诺。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM