最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - JavaScript checking if resource is reachable with fetch - Stack Overflow

programmeradmin17浏览0评论

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 = ".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.

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.sstatic.net/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.

Share Improve this question edited Mar 17, 2022 at 10:15 VLAZ 29k9 gold badges62 silver badges84 bronze badges asked Mar 24, 2017 at 9:24 SaAtomicSaAtomic 7491 gold badge12 silver badges30 bronze badges 8
  • 5 Possible duplicate of How do I return the response from an asynchronous call? – Yury Tarabanko Commented Mar 24, 2017 at 9: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 Commented Mar 24, 2017 at 9:35
  • @adeneo would it be better/ would it work at all, if I used a loop, with setTimeout using increasing timeout times? – SaAtomic Commented Mar 24, 2017 at 9: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 Commented Mar 24, 2017 at 9: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 Commented Mar 24, 2017 at 9:40
 |  Show 3 more comments

5 Answers 5

Reset to default 4

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.sstatic.net/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.

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.sstatic.net/Ya15i.jpg')
  .then(found => {
    console.log(found ? 'Reachable': 'Unreachable')
  })

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.sstatic.net/Ya15i.jpg');

fetch(myRequest,myInit).then(function(response) {
  ... 
});

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.sstatic.net/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)

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.sstatic.net/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'});
}
发布评论

评论列表(0)

  1. 暂无评论