JavaScript checking if resource is reachable with

2019-05-02 07:10发布

问题:

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.

回答1:

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.



回答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)


回答3:

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'});
}


回答4:

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')
  })


回答5:

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) {
  ... 
});