G
G
Grigory Vasilkov2017-09-15 15:23:49
JavaScript
Grigory Vasilkov, 2017-09-15 15:23:49

How to write a synchronous wait in JS?

I wrote this code:

var show_timer = false;
var waits = {};
var default_limit_burst = 12;
var default_limit_minute = 200;
var limit_burst = 12;
var limit_minute = 200;
function wait(name, timeout) {
  let obj = {};

  // init
  waits[name] = waits[name] || {};
  waits[name].irv = waits[name].irv || [];
  waits[name].ps = waits[name].ps || [];

  // create interval
  obj.promise = new Promise((resolve, reject) => {
    obj.left = timeout;
    obj.interval = setInterval(() => {
      obj.left--;
      if (show_timer) console.log('Timer `' + name + '`. Left: ' + obj.left);
      if (obj.left <= 0) {
        clearInterval(obj.interval);
        resolve([null, 'OK']);
      }
    }, 1200);
  }).catch(frpd);

  // get old keys
  var keys = array_keys(waits[name].ps) || [];

  // save new items
  waits[name].irv.push(obj.interval);
  waits[name].ps.push(obj.promise);

  // try to resolve all old promises/intervals except current
  try {
    keys.forEach(v => {
      clearInterval(waits[name].irv[v]);
      Promise.resolve(waits[name].ps[v]);
    });
  } catch (e) {
    frpd(e);
  }

  // return batch promise
  return Promise.all(waits[name].ps);
}

No matter how many times I call wait('timer1', 5) - it should add everything together into one array of promises and give the result only when everything is over, well, as I expected.
However, a funny effect is observed, namely when the timers run out - for some reason the program falls into process.exit () - the whole script ends, although there are still many requests ahead.
The code is written like this
co(function* () {
  // ждем 5 сек
  if (limit < 0)  yield wait('timer1', 5);
});

The trick is not to just wait in the code for this, you can write
yield new Promise(resolve => setTimeout(resolve, 5000));

The joke is that some requests are made in batches, the so-called batch requests.
And in a pack, there are not always limit requests! There can be, for example, 12 requests, and now the limit is only 5. Thus, you need to make 5, and when the timer resolves, make the remaining 7.
It will turn out
var ps = [];
for (let i in requests) {
  ps.push(
   co(function* () {
     if (limit < 0)  yield wait('timer1', 5);    
   });
  );
}
return Promise.all(ps);

In this case, a batch of timers is started. And they should all end together, because. one timer here you will not start it seems to me.
Please correct my code or direct me in the right direction, maybe the logic is initially wrong.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
G
Grigory Vasilkov, 2017-09-15
@gzhegow

So far, it has worked like this:
instead of starting a timer for each request in a pack, I wrote something like this:

co(function* () {
  var limit_burst = default_limit_burst;
  var limit_minute = default_limit_minute;
  var limit_day = default_limit_day;

  // getting records from somewhere
  var records = get_the_records();

  records = yield co(function* () {
    var records_done = [];
    while (records.length) {
      let limit = Math.min(limit_burst, limit_minute, limit_day);
      let batch = records.splice(0, limit);
      
      // update limits
      limit_burst -= batch.length;
      limit_minute -= batch.length;
      limit_day -= batch.length;

      for (let i in batch) {
        // ... request
      }

      // refresh limits from response headers if available
      yield Promise.all(ps).then(results => {
        var current_limit_burst = default_limit_burst;
        var current_limit_minute = default_limit_minute;
        var current_limit_day = default_limit_day;

        // save results
        records_done = results.map(r => {
          if (r[0] !== null) return console.error(r[1]) || null;
          current_limit_burst = Math.min(current_limit_burst, r[2][0]);
          current_limit_minute = Math.min(current_limit_minute, r[2][1]);
          current_limit_day = Math.min(current_limit_day, r[2][2]);
          return r[1];
        }).filter(r => r!==null);

        limit_burst = current_limit_burst;
        limit_minute = current_limit_minute;
        limit_day = current_limit_day;
      });

      if (limit_burst <= 0) yield new Promise(r => setTimeout(r, 5000));
      if (limit_minute <= 0) yield new Promise(r => setTimeout(r, 60000));
      if (limit_day <= 0) yield new Promise(r => setTimeout(r, 86400000));
    }

    return records_done;
  });
});

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question