0

So I have an API management method that handles batches of queries / rate limits and my code was just ceasing to execute silently. The program is being killed (I know it's not just failing to iterate because there's a constant setInterval func running) without throwing any error.

So I started tracing the execution with "debug 1", "debug 2" console logs and got to this output:

...repetition of this pattern
debug 8.3 52 62
debug 2 https://api.tradier.com/v1/markets/options/expirations?symbol=ORLY&includeAllRoots=true
debug 2.5 {"expirations":{"date":["2020-05-15","2020-06-19","2020-08-21","2020-11-20"]}}
debug 4
debug 5.1
debug 6
debug 7
debug 8.3 35 62
debug 2 https://api.tradier.com/v1/markets/options/expirations?symbol=VRTX&includeAllRoots=true
debug 2.5 {"expirations":{"date":["2020-04-24","2020-05-01","2020-05-08","2020-05-15","2020-05-22","2020-05-29","2020-06-05","2020-06-19","2020-07-17","2020-10-16","2021-01-15","2022-01-21"]}}
debug 4
debug 5.1
debug 6
debug 7
debug 8.1
resolve executed

and the 8.1 debug log is here in my code:

console.log("debug 8.1")
resolve(queryResponses);
console.log("resolve executed");

which should lead to:

apiHandler.dynamicQuery(args).then((expirations) => {
        console.log("debug 9")
        ... snip ...
}

And I can't figure out where to even begin to debug what's going on between resolve() and .then() to cause a silent failing error. How can I proceed to debug this issue?

Relevant methods:

// api handler class

dynamicQuery(queryArr, i = 0, queryResponses = []) {
    return new Promise((resolve, reject) => {
      this.query(...queryArr[i].args).then(result => {
        queryResponses.push(result);
        if (queryResponses.length == queryArr.length) {
          console.log("debug 7-pre")
          return resolve(queryResponses);
        }
        i++;
        let headers = result.response.headers;
        let availableRequests = parseInt(headers["x-ratelimit-available"]);
        let limitResetTime = parseInt(headers["x-ratelimit-expiry"]);
        let currentTime = parseInt((new Date()).getTime());
        let remainingTime = limitResetTime - currentTime;
        if (availableRequests > 0) {
          let requestChunkSize;
          let remainingQueries = queryArr.length - i - 1;
          if (remainingQueries < availableRequests) {
            requestChunkSize = remainingQueries;
          } else requestChunkSize = availableRequests;
          for (let j = 0; j <= requestChunkSize; j++) {
            this.query(...queryArr[i].args).then(result => {
              console.log("debug 7")
              queryResponses.push(result);
              if (queryResponses.length == queryArr.length) {
                console.log("debug 8.1")
                resolve(queryResponses);
                console.log("resolve executed");
              } else if (j >= requestChunkSize) {
                console.log("debug 8.2", j, requestChunkSize)
                if (i < queryArr.length - 1) {
                  this.dynamicQuery(queryArr, i, queryResponses);
                } else {
                  return resolve(queryResponses);
                }
              } else {
                console.log("debug 8.3", j, requestChunkSize)
              }
            }).catch(err => console.log(err));
            i++;
          }
        } else {
          setTimeout(() => {
            this.dynamicQuery(queryArr, i, queryResponses);
          }, remainingTime + 50)
        }
      }).catch(err => console.log(err));
    });
  }

// manager class

getExpirations(symbolList, target, range) {
    return new Promise((resolve, reject) => {
      let args = [];
      symbolList.forEach(symbol => {
        args.push({
          args: ["/v1/markets/options/expirations", {
            "symbol": symbol,
            'includeAllRoots': 'true',
          }, false]
        });
      });
      apiHandler.dynamicQuery(args).then((expirations) => {
        console.log("debug 9")
        //console.log("expirations", expirations.expirations)
        //console.log("expirations", JSON.stringify(expirations, null, 1))
        let processedResult = []
        expirations.forEach(expiration => {
          let validExpirationData = [];
          let expirationData;
          let expirationWithinRange;
          //console.log("expiration.response.body", expiration.response.body)
          if (expiration.response.body.expirations) {
            expirationData = expiration.response.body.expirations.date;
            expirationData.forEach(date => {
              if (momentUtil.isWithinRange(date, target, range)) validExpirationData.push(date);
            });
          }
          processedResult.push({
            symbol: expiration.qs.symbol,
            expirations: validExpirationData
          });
        })
        resolve(processedResult);
      }).catch(err => console.log(err));
    });
  }

Edit:

If I run the script with forever, I get the log:

debug 8.1
resolve executed
error: Forever detected script was killed by signal: undefined
J.Todd
  • 707
  • 1
  • 12
  • 34
  • edited to add "resolve executed" log after the resolve call, proving that the resolve call itself doesn't seem to be crashing the program, but it does not trigger .then and the program does appear to crash after that, as explained in the question (because there's a setInterval func running) – J.Todd Apr 23 '20 at 17:02
  • 1
    https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it – Jonas Wilms Apr 23 '20 at 17:19
  • you do some recursive calls. I assume one of those calls actually logs and resolves, but as you do nothing with the returned promise you don't notice that. – Jonas Wilms Apr 23 '20 at 17:23
  • @JonasWilms but why wouldnt any error be logging? Silent crashes are odd – J.Todd Apr 23 '20 at 17:35
  • it doesnt crash. You are resolving the wrong promise. – Jonas Wilms Apr 23 '20 at 17:36
  • @JonasWilms It does crash. Look at the forever logs: `error: Forever detected script was killed by signal: undefined` – J.Todd Apr 23 '20 at 17:46

0 Answers0