feat(watch): add support for `AbortController`
This PR adds support for a new option signal to the watch API in @vue/reactivity.
It accepts an AbortSignal. When provided, the watcher will be stopped once the corresponding AbortController is aborted.
This provides a more flexible way to stop multiple watchers at once by sharing a single AbortController.
Before:
const count = ref(0)
const cb1 = () => {}
const cb2 = () => {}
const stop1 = watch(count, cb1)
const stop2 = watch(count, cb2)
stop1()
stop2()
After:
const count = ref(0)
const controller = new AbortController()
const cb1 = () => {}
const cb2 = () => {}
watch(count, cb1, { signal: controller.signal })
watch(count, cb2, { signal: controller.signal })
controller.abort()
Summary by CodeRabbit
-
New Features
- Added AbortController-based cancellation for reactive watchers. You can pass a signal to cancel one or multiple watchers at once. Works across effect and source-based watchers, including post and sync variants. Existing behavior remains unchanged unless a signal is provided.
-
Tests
- Added tests verifying that aborting a shared signal stops all associated watchers and that no further updates occur after cancellation.
Walkthrough
Adds AbortSignal-based cancellation to reactivity watchers. The watch API now accepts an optional signal to stop a watcher on abort. Runtime-core exposes a BaseWatchEffectOptions with signal and updates watchPostEffect/watchSyncEffect signatures. Tests verify abort-driven cancellation for watch, watchEffect, and multiple watches sharing one signal.
Changes
| Cohort / File(s) | Summary |
|---|---|
Reactivity: AbortSignal support in watchpackages/reactivity/src/watch.ts |
Adds signal?: AbortSignal to WatchOptions; registers abort listener to stop the watch handle when signaled. Existing behavior unchanged when no signal provided. |
Reactivity tests: watch abort behaviorpackages/reactivity/__tests__/watch.spec.ts |
Adds test ensuring two watches sharing one AbortSignal are both stopped after abort. |
Runtime-core: options surface for watch effectspackages/runtime-core/src/apiWatch.ts |
Introduces BaseWatchEffectOptions with optional signal; WatchEffectOptions extends it; updates watchPostEffect and watchSyncEffect signatures to accept BaseWatchEffectOptions. |
Runtime-core tests: aborting watchers/effectspackages/runtime-core/__tests__/apiWatch.spec.ts |
Adds tests verifying AbortController stops watchEffect and watch watchers from reacting after abort. |
Sequence Diagram(s)
sequenceDiagram
autonumber
participant C as Caller
participant AC as AbortController
participant W as watch()
participant E as Reactive Effect
participant H as WatchHandle
C->>AC: const { signal } = new AbortController()
C->>W: watch(source, cb, { signal })
W->>E: create reactive effect
W-->>H: return handle (stop)
Note over E,H: Normal operation
E-->>C: on source change -> invoke cb
Note over AC,H: Abort path (new)
C->>AC: controller.abort()
AC-->>W: signal "abort" event
W->>H: H.stop()
H-->>E: teardown effect (unsubscribe)
Note over E: After abort: no further cb invocations
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Pre-merge checks (2 passed, 1 warning)
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title Check | ✅ Passed | The title succinctly and accurately describes the primary change, namely adding support for AbortController to the watch API in the reactivity package, and follows the conventional commit format with scope and type. |
| Description Check | ✅ Passed | The description clearly outlines the new signal option for the watch API, explains its behavior with AbortController, and provides before-and-after code examples that directly relate to the changeset. |
Poem
I twitch my ears at signals’ call,
A whisper: “Abort!”—and I stop, that’s all.
Two watches nap beneath one tree,
One puff of wind, both wander free.
I thump the ground—tests pass, of course! 🐇
Now onward hop, with lighter force.
Pre-merge checks (2 passed, 1 warning)
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title Check | ✅ Passed | The title succinctly and accurately describes the primary change, namely adding support for AbortController to the watch API in the reactivity package, and follows the conventional commit format with scope and type. |
| Description Check | ✅ Passed | The description clearly outlines the new signal option for the watch API, explains its behavior with AbortController, and provides before-and-after code examples that directly relate to the changeset. |
Poem
I twitch my ears at signals’ call,
A whisper: “Abort!”—and I stop, that’s all.
Two watches nap beneath one tree,
One puff of wind, both wander free.
I thump the ground—tests pass, of course! 🐇
Now onward hop, with lighter force.
Pre-merge checks (2 passed, 1 warning)
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title Check | ✅ Passed | The title succinctly and accurately describes the primary change, namely adding support for AbortController to the watch API in the reactivity package, and follows the conventional commit format with scope and type. |
| Description Check | ✅ Passed | The description clearly outlines the new signal option for the watch API, explains its behavior with AbortController, and provides before-and-after code examples that directly relate to the changeset. |
Poem
I twitch my ears at signals’ call,
A whisper: “Abort!”—and I stop, that’s all.
Two watches nap beneath one tree,
One puff of wind, both wander free.
I thump the ground—tests pass, of course! 🐇
Now onward hop, with lighter force.
Pre-merge checks (2 passed, 1 warning)
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title Check | ✅ Passed | The title succinctly and accurately describes the primary change, namely adding support for AbortController to the watch API in the reactivity package, and follows the conventional commit format with scope and type. |
| Description Check | ✅ Passed | The description clearly outlines the new signal option for the watch API, explains its behavior with AbortController, and provides before-and-after code examples that directly relate to the changeset. |
Poem
I twitch my ears at signals’ call,
A whisper: “Abort!”—and I stop, that’s all.
Two watches nap beneath one tree,
One puff of wind, both wander free.
I thump the ground—tests pass, of course! 🐇
Now onward hop, with lighter force.
Pre-merge checks (2 passed, 1 warning)
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title Check | ✅ Passed | The title succinctly and accurately describes the primary change, namely adding support for AbortController to the watch API in the reactivity package, and follows the conventional commit format with scope and type. |
| Description Check | ✅ Passed | The description clearly outlines the new signal option for the watch API, explains its behavior with AbortController, and provides before-and-after code examples that directly relate to the changeset. |
Poem
I twitch my ears at signals’ call,
A whisper: “Abort!”—and I stop, that’s all.
Two watches nap beneath one tree,
One puff of wind, both wander free.
I thump the ground—tests pass, of course! 🐇
Now onward hop, with lighter force.
Pre-merge checks (2 passed, 1 warning)
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title Check | ✅ Passed | The title succinctly and accurately describes the primary change, namely adding support for AbortController to the watch API in the reactivity package, and follows the conventional commit format with scope and type. |
| Description Check | ✅ Passed | The description clearly outlines the new signal option for the watch API, explains its behavior with AbortController, and provides before-and-after code examples that directly relate to the changeset. |
Poem
I twitch my ears at signals’ call,
A whisper: “Abort!”—and I stop, that’s all.
Two watches nap beneath one tree,
One puff of wind, both wander free.
I thump the ground—tests pass, of course! 🐇
Now onward hop, with lighter force.
Pre-merge checks (2 passed, 1 warning)
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title Check | ✅ Passed | The title succinctly and accurately describes the primary change, namely adding support for AbortController to the watch API in the reactivity package, and follows the conventional commit format with scope and type. |
| Description Check | ✅ Passed | The description clearly outlines the new signal option for the watch API, explains its behavior with AbortController, and provides before-and-after code examples that directly relate to the changeset. |
Poem
I twitch my ears at signals’ call,
A whisper: “Abort!”—and I stop, that’s all.
Two watches nap beneath one tree,
One puff of wind, both wander free.
I thump the ground—tests pass, of course! 🐇
Now onward hop, with lighter force.
Pre-merge checks (2 passed, 1 warning)
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title Check | ✅ Passed | The title succinctly and accurately describes the primary change, namely adding support for AbortController to the watch API in the reactivity package, and follows the conventional commit format with scope and type. |
| Description Check | ✅ Passed | The description clearly outlines the new signal option for the watch API, explains its behavior with AbortController, and provides before-and-after code examples that directly relate to the changeset. |
Poem
I twitch my ears at signals’ call,
A whisper: “Abort!”—and I stop, that’s all.
Two watches nap beneath one tree,
One puff of wind, both wander free.
I thump the ground—tests pass, of course! 🐇
Now onward hop, with lighter force.
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title Check | ✅ Passed | The title succinctly describes the core feature addition—support for AbortController in the watch API—using clear, concise phrasing that directly reflects the main change introduced by the pull request. |
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
✨ Finishing touches
- [ ] 📝 Generate Docstrings
🧪 Generate unit tests
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
Size Report
Bundles
| File | Size | Gzip | Brotli |
|---|---|---|---|
| compiler-dom.global.prod.js | 84 kB | 29.8 kB | 26.3 kB |
| runtime-dom.global.prod.js | 104 kB (+71 B) | 39.2 kB (+41 B) | 35.3 kB (+19 B) |
| vue.global.prod.js | 162 kB (+71 B) | 59.3 kB (+40 B) | 52.9 kB (+14 B) |
Usages
| Name | Size | Gzip | Brotli |
|---|---|---|---|
| createApp (CAPI only) | 47.2 kB | 18.4 kB | 16.9 kB |
| createApp | 56.1 kB (+71 B) | 21.6 kB (+30 B) | 19.7 kB (+26 B) |
| createApp + vaporInteropPlugin | 68.7 kB (+71 B) | 25.9 kB (+31 B) | 23.7 kB (+62 B) |
| createVaporApp | 20.9 kB | 8.26 kB | 7.57 kB |
| createSSRApp | 60.4 kB (+71 B) | 23.4 kB (+27 B) | 21.3 kB (+32 B) |
| defineCustomElement | 61.1 kB (+71 B) | 23.2 kB (+29 B) | 21.1 kB (+33 B) |
| overall | 70.5 kB (+71 B) | 26.9 kB (+43 B) | 24.5 kB (+51 B) |
@vue/compiler-core
npm i https://pkg.pr.new/@vue/compiler-core@13861
@vue/compiler-dom
npm i https://pkg.pr.new/@vue/compiler-dom@13861
@vue/compiler-sfc
npm i https://pkg.pr.new/@vue/compiler-sfc@13861
@vue/compiler-ssr
npm i https://pkg.pr.new/@vue/compiler-ssr@13861
@vue/compiler-vapor
npm i https://pkg.pr.new/@vue/compiler-vapor@13861
@vue/reactivity
npm i https://pkg.pr.new/@vue/reactivity@13861
@vue/runtime-core
npm i https://pkg.pr.new/@vue/runtime-core@13861
@vue/runtime-dom
npm i https://pkg.pr.new/@vue/runtime-dom@13861
@vue/runtime-vapor
npm i https://pkg.pr.new/@vue/runtime-vapor@13861
@vue/server-renderer
npm i https://pkg.pr.new/@vue/server-renderer@13861
@vue/shared
npm i https://pkg.pr.new/@vue/shared@13861
vue
npm i https://pkg.pr.new/vue@13861
@vue/compat
npm i https://pkg.pr.new/@vue/compat@13861
commit: 2ceaf78
Can you do the same for effectScope? Or perhaps the implementation of effectScope is enough and watch doesn't need this.
We should probably benchmark this.
It would be good to have this for effectScope and watch. This would allow us to get the signal with getCurrentScope.
const {signal} = getCurrentScope()
fetch(url, {signal})
We should probably benchmark this.
It would be good to have this for
effectScopeandwatch. This would allow us to get thesignalwithgetCurrentScope.const {signal} = getCurrentScope() fetch(url, {signal})
That looks awesome! I’ve opened a new PR for effectScope — I’m not entirely sure if this aligns with what you had in mind, so feel free to take a look and leave your feedback there!
Thank you all for the reviews and suggestions! 💚
Deploy Preview for vue-sfc-playground failed. Why did it fail? →
| Name | Link |
|---|---|
| Latest commit | 04ce2385acf1ae46aeebb64fd15092809a2d74ce |
| Latest deploy log | https://app.netlify.com/projects/vue-sfc-playground/deploys/68d2375cdbe1320008f84f7d |
LGTM. Note: Documentation at https://vuejs.org/api/reactivity-core.html#watch needs to be updated when this feature is landed.