在 Cloudflare Worker 上响应后是否可以执行代码?

Is it possible to execute code after responding on a Cloudflare Worker?

我开始使用 Cloudflare 工作,我正面临第一个挑战。

我有一些缓存数据,一旦我通过 'fetch' 事件请求它,我就会检索这些数据。我想在响应此提取后,更新请求的缓存数据。

我从第三方获得这些数据 API。

工作流程如下。

  1. 获取请求并使用旧的缓存项进行响应
  2. 之后刷新所请求项目的缓存

我正在考虑使用 cron 计时器并填充要更新的项目队列,但我认为该列表不会持久化。

理想的方法是拥有某种自定义事件,我可以在 return 响应之前注册并触发,因此当我 return 更新旧缓存数据时,刷新缓存作业已经在执行。

我当前的代码正在运行(2 尚未实现),我不会发布代码,因为我只是想知道一些关于如何完成此刷新的理论。

提前致谢!

waitUntil 中做出承诺,然后 return 您的回应,有关详细信息,请参阅:https://developers.cloudflare.com/workers/runtime-apis/fetch-event#waituntil

如果您在服务工作者模式下部署了典型的工作者处理程序:

addEventListener("fetch", (event) => {
    event.respondWith(
        handleRequest(event).catch(
            (err) => new Response(err.stack, { status: 500 })
        )
    );
});

并且在该函数中 handleRequest 您检查了是否已经有缓存的响应,如果没有则默认为虚拟响应

async function handleRequest(event) {
    let response = await caches.default.match(event.request)

    response = response ? new Response(response.body, response) : new Response('not found on cache ' + Date.now())
    response.headers.set('response-datetime', new Date().toISOString())
    return response
}

(为了这个例子,我正在从缓存的响应中创建一个新的、可修改的响应,如果有的话)

FetchEvent.waitUntil 是一种期望将 promise 作为参数的方法。您不需要等待 event.waitUntil 或其参数来解决。就像你做的一样

fetch(<url>).then(()=>console.log('fetch resolved'))
return response

setTimeout(()=>console.log('timeout callback'),500)
return response

之前的函数调用不会延迟响应。区别在于调用

 event.waitUntil(getFreshInfoFromSomewhere(event.request))

将 worker 的生命周期延长到响应之后,直到它的 promise 参数被解析(当然,但仍然受到运行时时间限制)。

负责获取新响应的函数由您决定,我制作了一个将虚拟响应放入缓存中的函数,在请求后 500 毫秒:

/**
 * @param {Request} request 
 * @returns {<Promise<void>>}
 */
function getFreshInfoFromSomewhere(request) {
  return new Promise(resolve => {
    let response = new Response('response-cached-on ' + new Date().toLocaleTimeString())
    setTimeout(() => caches.default.put(request, response).then(resolve), 500)
  })
}

/**
 * @param {FetchEvent} event 
 * @returns <Promise<Response>>
 */
async function handleRequest(event) {
  let response = await caches.default.match(event.request),
    currentDatetime = new Date().toLocaleTimeString()
  event.waitUntil(getFreshInfoFromSomewhere(event.request))
  response = response ? new Response((await response.text()) + ', current datetime is ' + currentDatetime, response) : new Response('not found on cache at ' + currentDatetime)
  response.headers.set('response-datetime', currentDatetime)
  response.headers.set('cache-control', 'no-cache')
  return response
}


addEventListener("fetch", (event) => {
  event.respondWith(
    handleRequest(event).catch(
      (err) => new Response(err.stack, {
        status: 500
      })
    )
  );
});

您可以在 https://examples.ffflabs.com/cached

看到它正在运行

(这是一个非常低效的实现,我需要强调底层时间线)