From f0e423f9c351b22bccd3ed3aa57e1bbfd4ada68a Mon Sep 17 00:00:00 2001 From: ygj6 Date: Wed, 7 Jul 2021 18:08:12 +0800 Subject: [PATCH] fix(watch): errors thrown in the asynchronous callback function in watch will not be caught. (#751) --- src/apis/watch.ts | 10 +++++----- test/apis/watch.spec.js | 32 +++++++++++++++++++++++++++++++- test/helpers/mockWarn.ts | 4 ++-- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/apis/watch.ts b/src/apis/watch.ts index c3cb5a88..abddb89c 100644 --- a/src/apis/watch.ts +++ b/src/apis/watch.ts @@ -235,14 +235,14 @@ function createWatcher( ) { return fn } - return (((...args: any[]) => + return ((...args: any[]) => queueFlushJob( vm, () => { fn(...args) }, flushMode as 'pre' | 'post' - )) as any) as T + )) as any as T } // effect watch @@ -320,7 +320,7 @@ function createWatcher( const applyCb = (n: any, o: any) => { // cleanup before running cb again runCleanup() - cb(n, o, registerCleanup) + return cb(n, o, registerCleanup) } let callback = createScheduler(applyCb) if (options.immediate) { @@ -330,10 +330,10 @@ function createWatcher( let shiftCallback = (n: any, o: any) => { shiftCallback = originalCallback // o is undefined on the first call - applyCb(n, isArray(n) ? [] : o) + return applyCb(n, isArray(n) ? [] : o) } callback = (n: any, o: any) => { - shiftCallback(n, o) + return shiftCallback(n, o) } } diff --git a/test/apis/watch.spec.js b/test/apis/watch.spec.js index eeb323e6..ab946e11 100644 --- a/test/apis/watch.spec.js +++ b/test/apis/watch.spec.js @@ -1,7 +1,16 @@ const Vue = require('vue/dist/vue.common.js') -const { ref, reactive, watch, watchEffect, set } = require('../../src') +const { + ref, + reactive, + watch, + watchEffect, + set, + nextTick, +} = require('../../src') +const { mockWarn } = require('../helpers') describe('api/watch', () => { + mockWarn(true) const anyFn = expect.any(Function) let spy beforeEach(() => { @@ -587,6 +596,27 @@ describe('api/watch', () => { expect(spy).toHaveBeenNthCalledWith(2, [3, 1], [2, 1]) expect(spy).toHaveBeenNthCalledWith(3, [3, 3], [3, 1]) }) + + it('config.errorHandler should capture render errors', async () => { + new Vue({ + setup() { + const a = ref(1) + watch( + a, + async () => { + throw new Error('userWatcherCallback error') + }, + { immediate: true } + ) + return { + a, + } + }, + template: `
{{a}}
`, + }).$mount() + await nextTick() + expect(`userWatcherCallback error`).toHaveBeenWarned() + }) }) describe('Out of setup', () => { diff --git a/test/helpers/mockWarn.ts b/test/helpers/mockWarn.ts index 63d23728..500238b9 100644 --- a/test/helpers/mockWarn.ts +++ b/test/helpers/mockWarn.ts @@ -15,7 +15,7 @@ export function mockWarn(asError = false) { toHaveBeenWarned(received: string) { asserted.add(received) const passed = warn.mock.calls.some( - (args) => args[0].indexOf(received) > -1 + (args) => args[0].toString().indexOf(received) > -1 ) if (passed) { return { @@ -91,7 +91,7 @@ export function mockWarn(asError = false) { .map((args) => args[0]) .filter((received) => { return !assertedArray.some((assertedMsg) => { - return received.indexOf(assertedMsg) > -1 + return received.toString().indexOf(assertedMsg) > -1 }) }) warn.mockRestore()