6

I'm basically just trying to verify if a resource is reachable from the executing client. I can not use XHR, because the target resource doesn't allow that.

I'm pretty new to JS and am currently working with this ( executable here ):

    var done = false;
    var i = 1;
    var t = "https://i.stack.imgur.com/Ya15i.jpg";

    while(!done && i < 4)
    {
      console.log("try "+i);

      done = chk(t);
      sleep(1000);

      i = i+1;

      if (done)
      {
        console.log("Reachable!");
         break;
      }
      else
      {
         console.log("Unreachable.");
      }
    }

  function chk(target)
  {
    console.log("checking "+target)
    fetch(target, {mode: 'no-cors'}).then(r=>{
    return true;
    })
    .catch(e=>{
    return false;
    });
  }

  // busy fake sleep
  function sleep(s)
  {
      var now = new Date().getTime();
      while(new Date().getTime() < now + s){ /* busy sleep */ } 
  }

I was expecting this code to check for the resource, print the result, then wait for a sec. Repeat this until 3 tries were unsuccessful or one of them was successful.

Instead the execution blocks for a while, then prints all of the console.logs at once and the resource is never reachable (which it is).

I do know that the fetch operation is asynchronous, but I figured if I previously declare done and implement a sleep it should work. In the worst case, the while loop would use the previously declared done.

How do I achieve the described behavior? Any advice is welcome.

VLAZ
  • 26,331
  • 9
  • 49
  • 67
SaAtomic
  • 619
  • 1
  • 12
  • 29
  • 5
    Possible duplicate of [How do I return the response from an asynchronous call?](http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Yury Tarabanko Mar 24 '17 at 09:34
  • Your `sleep` function is blocking, that's really not what you want to do with something like this, you want it async, and then just create some sort of queue system that can handle that – adeneo Mar 24 '17 at 09:35
  • @adeneo would it be better/ would it work at all, if I used a loop, with `setTimeout` using increasing timeout times? – SaAtomic Mar 24 '17 at 09:36
  • 1
    You don't want `setTimeout` either, just use the callbacks for fetch, and create a callback for when all requests are done – adeneo Mar 24 '17 at 09:38
  • 1
    Also, `fetch()` won’t fail on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally ! – adeneo Mar 24 '17 at 09:40
  • @adeneo actually, that's quite OK, I only want to verify if the webserver is reachable, not the specific resource. – SaAtomic Mar 24 '17 at 09:41
  • So it doesn't matter that the image returns 404, just that the network is down etc – adeneo Mar 24 '17 at 09:41
  • right, I just want to verify if a client is able to reach a specific webserver – SaAtomic Mar 24 '17 at 09:42

5 Answers5

3

The main problem is that you are trying to return from callback. That makes no sense. But fetch is Promise based request you can use Promise to simulate delays as well

Something like this should do the trick

// promise based delay
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout))

// check if target can be fetched
const check = target => fetch(target, {...})
    .then(response => response.ok)

const ping = (target, times = 3, timeout = 1000) => check(target)
  .then(found => {
    if(!found && times) { // still can check
      // wait then ping one more time
      return delay(timeout).then(() => ping(target, times - 1, timeout))
    }

    return found
  })

ping('https://i.stack.imgur.com/Ya15i.jpg')
  .then(found => {
    console.log(found ? 'Reachable': 'Unreachable')
  })
Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
3

Your sleep function is blocking, what you really want is a recursive function that returns a promise after checking the url n times with a delay of y seconds etc.

Something like this

function chk(target, times, delay) {
    return new Promise((res, rej) => {                       // return a promise

        (function rec(i) {                                   // recursive IIFE
            fetch(target, {mode: 'no-cors'}).then((r) => {   // fetch the resourse
                res(r);                                      // resolve promise if success
            }).catch( err => {
                if (times === 0)                             // if number of tries reached
                    return rej(err);                         // don't try again

                setTimeout(() => rec(--times), delay )       // otherwise, wait and try 
            });                                              // again until no more tries
        })(times);

    });
}

To be used like this

var t = "https://i.stack.imgur.com/Ya15i.jpg";

chk(t, 3, 1000).then( image => {
    console.log('success')
}).catch( err => {
    console.log('error')
});

And note that this does not fail on 404 or 500, any response is a successful request.

adeneo
  • 312,895
  • 29
  • 395
  • 388
  • I'm still trying to wrap my head around JS - thank you very much for the additional comments and the usage! – SaAtomic Mar 24 '17 at 14:24
  • Is there a way to assign boolean values instead of logging success/error in chk and using this value after the function? I saw that if I add a log before and after the chk function, both are immediately printed and then the success/error is printed. – SaAtomic Mar 24 '17 at 14:27
  • No, not really, as the requests are async, you should work *with* that async nature, and not try to block the thread to use the result outside the callbacks etc. – adeneo Mar 24 '17 at 15:01
  • just to make sure. If i assign a boolean variable, instead of the log in the chk function, the boolean is simply set after the chk func is one and will not be *unassigned* until then? – SaAtomic Mar 24 '17 at 15:22
  • You can do anything you want inside the `then` and `catch` calls, but you can't use the result of anything you set *outside* those callbacks, that's sorta what async means, you have to wait until the request is done, and there is a result to check. – adeneo Mar 24 '17 at 15:42
  • I was able to restructure my complete code, so I can correctly work with the async requests. Is there a way to configure a timeout for fetch? It takes 2 minutes+, if the resource is unreachable. – SaAtomic Mar 27 '17 at 11:22
  • I appears like this issue was addressed [on github](https://github.com/whatwg/fetch/issues/20#issuecomment-196113354), but I've never used `Promises` until now, so I'm a bit lost. – SaAtomic Mar 27 '17 at 11:57
2

Try this, Hope it works

var myHeaders = new Headers();
myHeaders.append('Content-Type', 'image/jpeg');

var myInit = { method: 'GET',
               headers: myHeaders,
               mode: 'no-cors',
               cache: 'default' };

var myRequest = new Request('https://i.stack.imgur.com/Ya15i.jpg');

fetch(myRequest,myInit).then(function(response) {
  ... 
});
Vinod kumar G
  • 639
  • 6
  • 17
  • Thank you, I will give it a try. But I'd be really grateful for a little more explanation here. – SaAtomic Mar 24 '17 at 09:35
  • Yes, for GET method we need headers to , Most of time it will fetch but dew to permission setting where the resource file is location needs header meta tag to give response. For more details check - https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch – Vinod kumar G Mar 24 '17 at 09:53
2

Your chk function returns undefined, you return true/false from promise callbacks not from container function.

You should use recursion and timeout in catch callback. It will be something like this:

var i = 0;
var done = false;
var t = "https://i.stack.imgur.com/Ya15i.jpg";
(function chk(target){
  console.log("checking "+target)
  fetch(target, {mode: 'no-cors'}).then(r=>{
   done = true;
   console.log("Reachable!");
  })
  .catch(e=>{
   console.log("Unreachable.");
   if(i<4){
     setTimeout(function(){
       chk(target)
     },1000)
   }
  });
})(t)
Anton Stepanenkov
  • 1,026
  • 8
  • 15
2

You can't return within a callback. When you do, it is the callback that is returning, not the parent function. If fact, the function chk is never returning anything.

What it sounds like you are intending to do is return the promise returned by fetch. And attempt to fetch three times.

Try this:

const numberOfTries =3;
currentTry = 1;
var t = "https://i.stack.imgur.com/Ya15i.jpg";

chk(t);

function tryCheck(resource, currentTry) {
  chk(resource).done(function(){
    console.log("Reachable!");
  }).catch(function(e) {
    console.log("Unreachable.");
    if (currentTry >= numberOfTries) return;
    sleep(1000);
    tryCheck(resource, currentTry + 1);
  });
}

function chk(resource) {
  console.log("checking "+target);
  return fetch(target, {mode: 'no-cors'});
}
Nathan
  • 505
  • 2
  • 8