[Solved] event.waitUntil() doesn't do anything

Consider this basic worker that needs to respond to the caller immedialty, then hand off the request to another API.

async function saveToDatabase(notification) {
  return fetch('https://example.com/api', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(notification, null, 0)
  });
}

async function saveRequest(request) {
  let body;

  const contentType = request.headers.get('content-type')
  if (contentType.includes('application/json')) {
    body = await request.json();
  }
  
  if (body) {
    return saveToDatabase(body);
  }
}

async function handleRequest(event) {
  if (event.request.method === 'POST') {
    event.waitUntil(saveRequest(event.request));
    // try {
    //   await saveRequest(event.request);
    // } catch (error) {
    //   return new Response(error.toString(), { status: 500 })
    // }
   
    return new Response(undefined, { status: 200 })
  }
  return new Response(undefined, { status: 405 })
}

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

The function in waitUntil is never executed. If however, I await the function as shown in the commented out code - it works. So the handling + posting obviously works, but I don’t want to force the caller to wait.

What’s the issue?

a quick read might be missing a await on the fetch.

I don’t await it because it can be returned direct form the async function as it, and the caller of saveToDatabase gets the fetch promise.

One of the quirks of the Service Worker specification is that It’s not possible to read event.request's body after having generated a response, so I expect that the call to request.json() is resulting in an exception.

A quick workaround would be to read the request body up front, then call event.waitUntil() and return the Response object.

Edit: I had thought this behavior was mandated by the Service Worker spec, but after searching for a reference, I’m not so sure. At any rate, what I described is accurate for our implementation – still researching why.

2 Likes

That solved it for me thank you @harris. I await loading the body before beginning processing the response or the waitUntil. I tested this with an setTimeout to exaggerate the delay and the request responded instantly, while the saveToDatabase function would now be called after. :white_check_mark:

async function handleRequest(event) {
  if (event.request.method === 'POST') {
    const body = await getBody(event.request);
    if (body) event.waitUntil(saveToDatabase(body)); // this might actually take a few seconds
    return new Response(undefined, { status: 200 }) // caller sees instant response regardless
  }
  return new Response(undefined, { status: 405 })
}