Non-await fetch() isn’t always working

workers

#1

Hello! In our Cloudflare workers, we’ve been finding that a non-await fetch() isn’t always sending the promised request before the worker’s returned Response.

Following this Debugging Tips example I monkey-patched console.log() to send its arguments to a server, but servers are only receiving the POST request some of the time.

Here’s a Cloudflare worker test case:

Use curl or similar to test outside of Cloudflare’s UI:
curl -i https://example.com/worker

View receipts of the POST request on PutsReq.com here:
https://putsreq.com/ZyjM4ubsAADQyGBDRJ08/inspect

In my trials, the POST request is received 100% of the time when the worker is tested via the Cloudflare Workers UI, but it’s quite inconsistent when tested from curl or similar. The worker always responds to the route as expected, but the POST request doesn’t always get sent.

Can you reproduce?


Cloudflare Workers Beta Feedback
#2

Hi @bertap,

You’re right – after analyzing this, we realized that monkey-patching console.log() won’t work in all cases. We’ll update the documentation to reflect that.

The reason it doesn’t work is that once a Response has been returned and its body has finished sending, then all other asynchronous tasks are canceled, including the fetch() in the monkey-patched console.log().

There is a way to opt out of this behavior: event.waitUntil(). If you need to keep an asynchronous task like this running in the background even after the response has finished sending, you can pass a promise to event.waitUntil(), and the runtime will keep asynchronous tasks started in that fetch event alive until the promise is settled. So, an alternate way to log might involve something like:

addEventListener('fetch', event => {
  event.respondWith(fetchAndFetch(event))
})

async function fetchAndFetch(event) {
  const content = `timestamp=${Date.now()}`

  // Actually a POST request.
  event.waitUntil(postLog(content))

  // Respond.
  return new Response(`Sent a POST request with content: ${content} \n`)
}

async function postLog() {
  return fetch('https://putsreq.com/ZyjM4ubsAADQyGBDRJ08', {
    method: 'POST',
    body: JSON.stringify([...arguments])
  })
}

Admittedly, this is not quite as aesthetic as console.log(), but it should be more robust.

Let me know if you’ve got any questions!
Harris


#3

Thanks @harris!

event.waitUntil() is the magical method we needed.

I think a ‘Quick responses with background tasks’ recipe would be a helpful addition to the Cloudflare Workers docs. :slight_smile: