开发者问题收集

遇到 Deno 意外的内部错误

2022-11-03
454

我通过 Deno TypeScript 中的 fetch-API 进行了大量提取。现在的问题是,我随机收到以下错误(无法被 try-catch 捕获):

error: Uncaught (in promise) TypeError: error sending request for url (https://www.googleapis.com/calendar/v3/calendars/****@group.calendar.google.com/events/?calendarId=****group.calendar.google.com): http2 error: stream error received: unexpected internal error encountered
        const result: Response = await fetch(url, {
                                 ^
    at async mainFetch (deno:ext/fetch/26_fetch.js:288:14)
    at async fetch (deno:ext/fetch/26_fetch.js:505:9)
    at async gFetchEvent (file:///home/****/my_script.ts:98:27)

我不知道如何修复它。这是一个 Deno 错误吗?

我安装了以下 deno 版本:

deno 1.25.2 (release, x86_64-unknown-linux-gnu)
v8 10.6.194.5
typescript 4.7.4

没有特定的代码行会破坏我的程序,只是过了一段时间(可能是几分钟,也可能是几天)我的程序就会因这个错误而崩溃。

它只出现在我的 Ubuntu 20.04.5 LTS vServer by 1blu 上,硬件规格如下:

H/W path  Device       Class      Description
=============================================
                       system     Computer
/0                     bus        Motherboard
/0/0                   memory     8GiB System memory
/0/1                   processor  AMD EPYC 7452 32-Core Processor
/1        veth09bb0e5  network    Ethernet interface
/2        veth0ab53b0  network    Ethernet interface
/3        veth62387d0  network    Ethernet interface
/4        veth7dbc5b2  network    Ethernet interface
/5        vethb66edc6  network    Ethernet interface

sudo lshw -short 的输出)

我主脚本中的代码:

try {
  await main()
} catch (e) {
  console.log(new Date(), e.stack)
  Deno.writeTextFileSync(`logs/${Date.now()}`, "" + e.stack)
}

我的主要函数

// this program checks changes in my school schedule and automatically puts them in my google calendar
export default async function main() {
  await Kantplaner.init()
  while (true) {
    // MKDate is just a class that extends Date for more functionallity, nothing special
    const start_day = new MKDate(Date.now())

    // repeats 14 times for the next 14 days
    for (let i = 0; i < 14; i++) {
      const date: MKDate = i ? start_day.nextDate(1) : start_day
      
      // get my schedule from my school's site
      const vplan: VPlan | null = await Indiware(date)
      if (!vplan) continue
      
      // fetch the existing events with google calendar api and check if something in the meantime changed
      const calendar = await Kantplaner.list_events(date)
      // male one big object containing all indices that were previously built by `await Indiware(date)`
      const GrandIndex = { ...vplan.data.KlassenIndex, ...vplan.data.LehrerIndex }
      for (const item of calendar.items) {
        const stundenNr = "some_string"
        const stundenMitDerID = GrandIndex[stundenNr]
        // if the event is not in my school's schedule anymore, delete it
        if (!stundenMitDerID) {
          await Kantplaner.delete_event(item.id)
          continue
        }
        // for every other event check differences and update the corresponding Google event
        // `stundenMitDerID` is an array of events with the same id (can happen at my school)
        for (let i = 0; i < stundenMitDerID.length; ++i) {
          // ... create update (doesn't matter)
          const update: Kantplaner.Update = {}
          await Kantplaner.update_event(item.id, update)
          // remove lesson from index to avoid another creation
          GrandIndex[stundenNr].splice(i)
          if (GrandIndex[stundenNr].length == 0) delete GrandIndex[stundenNr]
        }
      }
      // create every remainig event
      for (const stundenNr in GrandIndex) {
        const stundenMitDerID = GrandIndex[stundenNr]
        for (let i = 0; i < stundenMitDerID.length; ++i) {
          await Kantplaner.create_event({
            // event data
          })
        }
      }
    }
    // wait one minute to reduce unnecessary fetches
    await new Promise(r => setTimeout(r, 60_000))
  }
}

所有出现的 gFetchEvent

export async function list_events(date: MKDate, TIME_SHIFT: string): Promise<Calendar> {
    const calendar: Calendar | null = await gFetchEvent("/", "GET", {
        timeMin: date.format(`yyyy-MM-ddT00:00:00${TIME_SHIFT}`),
        timeMax: date.format(`yyyy-MM-ddT23:59:59${TIME_SHIFT}`),
    })
    if (!calendar) return EMPTY_CALENDAR

    let nextPageToken = calendar.nextPageToken
    while (nextPageToken) {
        const nextPage: Calendar = await gFetchEvent("/", "GET", {
            pageToken: nextPageToken,
            timeMin: date.format(`yyyy-MM-ddT00:00:00${TIME_SHIFT}`),
            timeMax: date.format(`yyyy-MM-ddT23:59:59${TIME_SHIFT}`),
        })
        calendar.items.push(...nextPage.items)
        nextPageToken = nextPage.nextPageToken
    }
    return calendar
}

export async function create_event(event: Event) {
    await gFetchEvent("/", "POST", { calendarId: CALENDAR_ID }, event)
}

export async function update_event(eventId: string, update: Update) {
    await gFetchEvent(`/${eventId}`, "PATCH", {
        sendUpdates: "none"
    }, update)
}

export async function delete_event(eventId: string) {
    await gFetchEvent(`/${eventId}`, "DELETE", {
        calendarId: CALENDAR_ID,
        eventId: eventId,
        sendUpdates: "none"
    })
}

我fetch:

async function gFetchEvent(urlPath: string, method: string, params?: { [key: string]: string }, body?: any) {
    if (!initiated) return null
    const url = new URL(CALENDAR_API_URL + urlPath)
    if (params) for (const key of Object.keys(params)) url.searchParams.append(key, params[key])

    const result: Response = await fetch(url, {
        headers: {
            Authorization: "Bearer " + access_token,
            Accept: "application/json",
            "Content-Type": "application/json"
        },
        method: method,
        body: JSON.stringify(body)
    })
    
    await new Promise(f => setTimeout(f, 100))
    if (result.ok) {
        const text = await result.text()
        if (text.length > 1) return JSON.parse(text)
        else return {}
    } else if (result.status == 403) {
        gFetchEvent(urlPath, method, params, body)
        return
    }
    return null
}

每个函数调用(例如 list_eventsupdate_event )都会隐式调用 gFetchEvent 函数(使用不同的 url 和 )。

2个回答

您缺少 await ,没有它,错误将无法正确冒泡到 main 函数中的 try/catch

if (result.ok) {
        const text = await result.text()
        if (text.length > 1) return JSON.parse(text)
        else return {}
    } else if (result.status == 403) {
        // You were missing an await here
        const res = await gFetchEvent(urlPath, method, params, body)
        return res;
        // or just
        // return gFetchEvent(urlPath, method, params, body);
    }

或者,如果您不想在该路径中使用 await ,您可以将 .catch 处理程序附加到 gFetchEvent 以防止 Uncaught Promise 错误。

Marcos Casagrande
2022-11-05

感谢 @Marcos Casagrande,我意识到我调用了一个异步函数,该函数在没有等待的情况下抛出错误。如果提取没有抛出错误,那就不是问题。

但是提取的一些错误(很可能是网络错误)导致了这个错误的抛出。

MrKleeblatt
2022-11-05