javascript - can async with fetch poll until a condition is met? (survive rejection) - Stack Overflow

Using fetch API and asyncawait, is it possible to continue polling indefinitely, regardless of availab

Using fetch API and async/await, is it possible to continue polling indefinitely, regardless of availability of a URL? I anticipate that a URL might bee available eventually, so I want to keep trying until a condition is met. Tried to e up with a minimum viable code sample and I'm not sure I pulled it off:

// this is just a placeholder. It will eventually be a function 
// that evaluates something real. 
// Assume validContinue gets updated elsewhere.      
function shouldContinue() {
    return validContinue;
}
      
async function wonderPoll(someUrl) {

  // just a delay mechanism
  function wait(ms = 1000) {
    return new Promise(resolve => {
      setTimeout(resolve, ms);
    });
  }

  // the actual individual poll
  async function pollingFunction(url) {

    const response = await fetch(url, {
      cache: 'no-store'
    });

    if (response.ok) {
      return response;
    } else {
      Promise.reject(response);
    }

  }

  // allegedly keep polling until condition is met. 
  // But the rejected Promise is breaking out!
  while (shouldContinue()) {
    await wait();
    result = await pollingFunction(someUrl);
  }
  
  // when the fetch hits a rejected state, we never get here!
  console.log('done with the while loop, returning last successful result')

  return result;
}

const sampleUrl = '.json?ip=8.8.8.8';
const sampleUrl2 = 'http://totallybroken_fo_sho';

// swap the URL to test
wonderPoll(sampleUrl)
  .then((result) => {
    console.log('got a result', result)
  })
  .catch((err) => {
    console.log('got an error', err)
  });

I see what's happening (I think). The parent call ultimately executes the polling function, which rejects on the Promise. The condition to continue is still theoretically met, but the rejection breaks out of the While loop and sends to rejection directly up. This propagates all the way up to the catch method of the original/initial Promise. It doesn't even hit any code that would have e after the While loop in the case of resolved Promises.

What I don't know is how to prevent that from happening. I think I don't understand the syntax for intercepting and resolving the promise. When I replace Promise.reject in the response parser with Promise.resolve(response), it still ends up rejecting up to the top.

If the URL I provide is valid, it will continue until the condition is no longer met.


Here's a fiddle: /

To use the fiddle, the "stop" button simulates the condition being met, and I've provided two different URLs that have to be manually swapped (by passing someUrl or someUrl2) to test.

Expected results:

  • with good URL, continuous polling (will have to dig into network in dev tools) until condition is met (by pressing Stop!) and then the calling function's 'then' can show the result.
  • with bad URL, continuous polling until condition is met, and then calling function's 'catch' shows the error

Actual results:

  • positive test case is OK
  • negative test case goes directly to the catch

Using fetch API and async/await, is it possible to continue polling indefinitely, regardless of availability of a URL? I anticipate that a URL might bee available eventually, so I want to keep trying until a condition is met. Tried to e up with a minimum viable code sample and I'm not sure I pulled it off:

// this is just a placeholder. It will eventually be a function 
// that evaluates something real. 
// Assume validContinue gets updated elsewhere.      
function shouldContinue() {
    return validContinue;
}
      
async function wonderPoll(someUrl) {

  // just a delay mechanism
  function wait(ms = 1000) {
    return new Promise(resolve => {
      setTimeout(resolve, ms);
    });
  }

  // the actual individual poll
  async function pollingFunction(url) {

    const response = await fetch(url, {
      cache: 'no-store'
    });

    if (response.ok) {
      return response;
    } else {
      Promise.reject(response);
    }

  }

  // allegedly keep polling until condition is met. 
  // But the rejected Promise is breaking out!
  while (shouldContinue()) {
    await wait();
    result = await pollingFunction(someUrl);
  }
  
  // when the fetch hits a rejected state, we never get here!
  console.log('done with the while loop, returning last successful result')

  return result;
}

const sampleUrl = 'https://get.geojs.io/v1/ip/country.json?ip=8.8.8.8';
const sampleUrl2 = 'http://totallybroken_fo_sho';

// swap the URL to test
wonderPoll(sampleUrl)
  .then((result) => {
    console.log('got a result', result)
  })
  .catch((err) => {
    console.log('got an error', err)
  });

I see what's happening (I think). The parent call ultimately executes the polling function, which rejects on the Promise. The condition to continue is still theoretically met, but the rejection breaks out of the While loop and sends to rejection directly up. This propagates all the way up to the catch method of the original/initial Promise. It doesn't even hit any code that would have e after the While loop in the case of resolved Promises.

What I don't know is how to prevent that from happening. I think I don't understand the syntax for intercepting and resolving the promise. When I replace Promise.reject in the response parser with Promise.resolve(response), it still ends up rejecting up to the top.

If the URL I provide is valid, it will continue until the condition is no longer met.


Here's a fiddle: https://jsfiddle/gregpettit/qf495bjm/5/

To use the fiddle, the "stop" button simulates the condition being met, and I've provided two different URLs that have to be manually swapped (by passing someUrl or someUrl2) to test.

Expected results:

  • with good URL, continuous polling (will have to dig into network in dev tools) until condition is met (by pressing Stop!) and then the calling function's 'then' can show the result.
  • with bad URL, continuous polling until condition is met, and then calling function's 'catch' shows the error

Actual results:

  • positive test case is OK
  • negative test case goes directly to the catch
Share asked Sep 30, 2020 at 3:37 Greg PettitGreg Pettit 10.8k5 gold badges57 silver badges73 bronze badges 4
  • I have considered just wrapping up a standard XMLHttpRequest with a Promise and explicitly resolving or intentionally NOT rejecting (on error)... it might be that fetch is too terse and inflexible for my needs? – Greg Pettit Commented Sep 30, 2020 at 3:44
  • I would always resolve with some sort of indication of success/failure in the resolved value ... then when "condition is met", return if successful, reject (throw) if failure – Jaromanda X Commented Sep 30, 2020 at 3:45
  • what does Promise.reject(response); do since you're throwing away the return value? – Ron Newb Commented Sep 30, 2020 at 3:47
  • Ron, I'm new to some of this, so it's possible you've caught me in a gaffe. I'd never put it past me. But my understanding is this: the fetch API actually treats coded "errors" as resolutions rather than rejections. But the response's "ok" property is false. So, I'm attempting to acmodate that as well. I could've done it wrong. ;-) – Greg Pettit Commented Sep 30, 2020 at 3:56
Add a ment  | 

4 Answers 4

Reset to default 3

You can try…catch it to prevent breaking out of loop.

while (shouldContinue()) {
  try {
    await wait();
    result = await pollingFunction(someUrl);
  } catch (e) {}
}

Change the code in while loop to try/catch so you can catch the error

result can hold a value when there's no error, or a reason when there is an error

Once the loop is stopped, you either return the value, or throw with the reason

As below

async function wonderPoll(someUrl) {

  // just a delay mechanism
  function wait(ms = 1000) {
    return new Promise(resolve => {
      setTimeout(resolve, ms);
    });
  }

  // the actual individual poll
  async function pollingFunction(url) {

    const response = await fetch(url, {
      cache: 'no-store'
    });

    if (response.ok) {
      return response;
    } else {
      Promise.reject(response);
    }

  }

  // allegedly keep polling until condition is met. But the rejected Promise is breaking out!
  while (shouldContinue()) {
    try {
      await wait();
      const value = await pollingFunction(someUrl);
      result = {value};
    } catch (reason) {
      result = {reason};
    }
  }
  // when the fetch hits a rejected state, we never get here!
  console.log('done with the while loop, returning last successful result')
  if (result.reason) {
    throw result.reason;
  }
  return result.value;
}

Running example https://jsfiddle/twkbo9pg/

the example includes status in the result, but that is unnecessary (I borrowed code from my Promise.allSettled polyfill and forgot to remove that property)

you might want to check out observable streams! If you're going to have a lot of data ing in over time, that's rxjs's whole thing.

There's actually a few ways to do this if this feels janky (it kinda does haha).

import { ajax } from "rxjs/ajax";
import { duration } from "moment-timezone"; // I copied this from some old code... whatever.
import { catchError, map, share, switchMap } from "rxjs/operators";

const baseUrl = "http://foo.bar"
const base = (method, headers = {}) => ({
  method,
  headers: {
    Accept: "application/json",
    ...headers,
  },
  crossDomain: true,
  withCredentials: true,
})

const ajaxGet = url => ajax({ ...base("GET"), url })

export const userEpic = timer(0, duration(5, "minutes").asMilliseconds()).pipe(
  switchMap(() =>
    ajaxGet(`${baseUrl}/users`).pipe(
      map(({ response }) => getUsersSuccess(response)),
      catchError(e => of(getUsersError(e))),
    )
  ),
  share()
)

Two things

 } else {
            Promise.reject(response);
        }

should return that. It's working "by accident" right now.

 } else {
            return Promise.reject(response);
        }

Secondly, result = await pollingFunction(someUrl); might want to add .catch to it: result = await pollingFunction(someUrl).catch(_=>null); or whatever can be tested for in the enclosing while

But I think you can simplify the whole thing thus:

export async function wonderPoll(someUrl) {
    while (shouldContinue()) {
        await wait();

        const response = await fetch(someUrl, { cache: 'no-store' });

        if (response.ok)
            return response;
    }
    return Promise.reject(); // only if !shouldContinue()
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744857628a4597502.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信