typescript - Hur hanterar man korrekt avvisningsfel i asynkrona händelselyssnare i Vuejs?


Vad är det rätta sättet att anropa en asynkron funktion från en eventlyssnare i vue.js, samtidigt som man säkerställer att eventuella avvisningsfel från promise bubbla upp via onErrorCaptured som vanligt?

Min första instinkt skulle vara att bara lägga till async:

window.addEventListener('click', async () => {
  await toggle() // error: no-misused-promise
})

Men Vue onErrorCaptured kommer inte att kallas om löftet avvisas och detta bryter mot eslint-regeln @typescript-eslint/no-misused-promise

Ett annat alternativ skulle vara att ta bort både await och async:

window.addEventListener('click', () => {
  toggle() // error: no-floating-promises
})

Detta leder dock fortfarande till ohanterade promise-avvisanden och bryter mot regeln @typescript-eslint/no-floating-promises.

Jag letade efter ett sätt att manuellt skicka avvisningsfelet för att hanteras av Vue onErrorCaptured, Vue exporterar en funktion "handleError" men det verkar vara endast för internt bruk.

window.addEventListener('click', () => {
  toggle().catch(handleError) // argument error
})

Mitt nuvarande tillfälliga lösning är att lagra felet i en ref och kasta det från en watchEffect-hanterare:

const error = ref<Error>()

window.addEventListener('click', () => {
  toggle().catch((err => { error.value = err })
})

watchEffect(() => {
  if (error.value) {
    throw new Error(error.value)
  }
})

Hur har andra människor löst detta?

Svar #1

Felen som inte kommer från Vue API (livscykelhakar, observatörer osv.) och händelselyssnare i mallar hanteras inte av Vue, detta måste göras av en användare.

Om asynkront fel behöver ignoreras måste detta uttryckligen göras eftersom det annars skulle resultera i ett ohandlat promise-fel:

window.addEventListener('click', async () => {
  try {
    await toggle()
  } catch {}
})

Eller :

window.addEventListener('click', () => {
  toggle().catch(() => {});
})

Felhanteirng kan göras ungefär på samma sätt som det gjordes här. Några saker som kan förbättras, new Error(error.value) skulle kassera den ursprungliga call stacken och leda till felaktigt formaterat felmeddelande om error.value är en instans av Error, vilket förväntas. Generellt är det att föredra att kasta om ett fel i detta fall.

För att hantera synkrona fel skulle en synkron övervakare vara mer lämplig.

Det kan extraheras till sådan komponerbar:

const useHandleComponentError = () => {
  const error = ref();

  watchSyncEffect(() => {
    if (error.value === undefined) {
      return;
    } else if (error.value instanceof Error) {
      throw error.value;
    } else {
      throw new Error(String(error.value))
    }
  });
  
  return fn => function errorHandler(...args) {
    let result;
    try {
      result = fn.apply(this, args);
    } catch (err) {
      // Sync error
      error.value = err;
    }
    
    if (result?.then) {
      // Promise-like async error
      result.then(null, err => {
        error.value = err;        
      });
    }
  };
};

Och används som:

const handleComponentError = useHandleComponentError();

window.addEventListener('click', handleComponentError(async () => {
  throw new Error('whatever')
}))

Relaterade artiklar