2

I am trying to poll an API endpoint (which gives an instant response) until it gives me a successful response and then save the result of the response

If an API has data for a given jobId it returns a string, and returns 400 status otherwise (I have full control over the API, so the behaviour can be adjusted). I want to poll the API endpoint every three seconds to see if it has an output.

I am a complete beginner with javascript and its derivatives, so below is the function I hacked

function getJobResults(jobId) {
          setTimeout(function() {
              $.get('/check_for_results/' + jobId, function(data, status) {
                      return data
                  })
                  .fail(function() {
                      getJobResults(jobId);
                  });
          }, 3000)
      }

How do I modify it so that I could do the following?

var expectedStringResult = getJobResults(jobId)

Currently, it does not return a result. Also, how could I rewrite the function to make it more sensible?

Thank you!

Denys
  • 4,287
  • 8
  • 50
  • 80
  • 3
    Just a small note, you may want to consider a long-polling approach or even a WebSocket approach instead of immediately telling the API consumer that the resource is not ready yet. It's like riding in a car with kids that keep asking "Are we there yet?" vs you explicitly telling them that you have arrived. – zero298 Aug 07 '19 at 14:36
  • @zero298 Thank you for the recommendation. Putting it on my to do list – Denys Aug 07 '19 at 15:08

2 Answers2

2

Using more modern async / await and fetch this could be easily written as:

 const timer = ms => new Promise(res => setTimeout(res, ms));

 async function poll(url) {
   let ok = false, response;
   while(!ok) {
     await timer(3000);
     try {
       (response = { ok } = await fetch(url));
     } catch(e) { /* keep looping */ }
   }

   return response;
}

Usable as:

 poll("https://stackoverflow.com")
   .then(res => res.text())
   .then(console.log);

Docs:

fetch()

async / await

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
1

The issue is because you cannot return anything from an async request. You instead need to use Promises or callbacks. The latter of which can be implemented quite simply by passing the function to execute when the AJAX request is successful in to the getJobResults() function, then calling it when required. Try this:

function getJobResults(jobId, callback) {
  setTimeout(function() {
    $.get('/check_for_results/' + jobId, function(data, status) {
      callback(data);
    }).fail(function() {
      getJobResults(jobId, callback);
    });
  }, 3000)
}

getJobResults('foo', function(data) {
  console.log('job found details below...');
  console.log(data);
});

It's worth noting that AJAX polling isn't a scalable solution, though. If you need this web application to scale then you should look in to using Web Sockets or Server Sent Events instead.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • 1
    @JoansWilms thank you. – Rory McCrossan Aug 07 '19 at 14:43
  • Thank you for this! Talking of scalability, could you give a VERY arbitrary estimation of a number of concurrent users, beyond which this approach would not scale? – Denys Aug 07 '19 at 14:46
  • 1
    That depends entirely on your server infrastructure and its performance, but compared to using the Observer pattern it will be an order of magnitude lower. – Rory McCrossan Aug 07 '19 at 14:47
  • ... It really depends on the usecase. WebSockets also have to be kept alive, but they are more leightweight than a HTTP request. So WebSockets will be more efficient when WebSocket.hearbeat * WebSocket.packetSize < Polling.heartbeat * Polling.packetSize – Jonas Wilms Aug 07 '19 at 15:20