or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asset-resolution.mdbuiltin-components.mdcomponents.mdcomposition-helpers.mddependency-injection.mderror-handling.mdhydration.mdindex.mdinternal-render-helpers.mdlifecycle.mdreactivity.mdscheduler-timing.mdssr-context.mdvdom-rendering.mdwatch-effects.md

scheduler-timing.mddocs/

0

# Scheduler & Timing

1

2

Vue's scheduler provides utilities for controlling the timing of updates and DOM operations, enabling precise control over when code executes relative to Vue's update cycle.

3

4

## Capabilities

5

6

### Next Tick Scheduling

7

8

Schedule callbacks to run after the next DOM update cycle.

9

10

```typescript { .api }

11

/**

12

* Defers callback execution until after the next DOM update cycle

13

* @param fn - Optional callback function to execute

14

* @returns Promise that resolves after the next tick

15

*/

16

function nextTick(fn?: () => void): Promise<void>;

17

```

18

19

**Usage Examples:**

20

21

```typescript

22

import { ref, nextTick } from "@vue/runtime-core";

23

24

const MyComponent = defineComponent({

25

setup() {

26

const count = ref(0);

27

const elementRef = useTemplateRef<HTMLElement>('element');

28

29

const updateAndRead = async () => {

30

// Update reactive data

31

count.value++;

32

33

// Wait for DOM to update

34

await nextTick();

35

36

// Now DOM reflects the new count

37

if (elementRef.value) {

38

console.log('Updated DOM content:', elementRef.value.textContent);

39

console.log('DOM height:', elementRef.value.offsetHeight);

40

}

41

};

42

43

// With callback style

44

const updateWithCallback = () => {

45

count.value++;

46

47

nextTick(() => {

48

console.log('DOM updated with new count:', count.value);

49

});

50

};

51

52

// Multiple nextTick calls

53

const chainedUpdates = async () => {

54

count.value = 1;

55

await nextTick();

56

console.log('First update complete');

57

58

count.value = 2;

59

await nextTick();

60

console.log('Second update complete');

61

};

62

63

return {

64

count,

65

elementRef,

66

updateAndRead,

67

updateWithCallback,

68

chainedUpdates

69

};

70

}

71

});

72

73

// Usage in lifecycle hooks

74

const LifecycleExample = defineComponent({

75

setup() {

76

const data = ref('initial');

77

78

onMounted(async () => {

79

// Change data

80

data.value = 'mounted';

81

82

// Wait for DOM update

83

await nextTick();

84

85

// Now safe to access updated DOM

86

console.log('DOM updated after mount');

87

});

88

89

return { data };

90

}

91

});

92

93

// Error handling with nextTick

94

const ErrorHandlingExample = defineComponent({

95

setup() {

96

const handleUpdate = async () => {

97

try {

98

// Some reactive updates

99

// ...

100

101

await nextTick();

102

103

// DOM operations that might fail

104

const element = document.querySelector('.my-element');

105

if (element) {

106

element.scrollIntoView();

107

}

108

} catch (error) {

109

console.error('Error after nextTick:', error);

110

}

111

};

112

113

return { handleUpdate };

114

}

115

});

116

```

117

118

### Post-flush Callback Queue

119

120

Queue callbacks to run after all component updates have been flushed.

121

122

```typescript { .api }

123

/**

124

* Queues a callback to run after all pending updates have been flushed

125

* @param cb - Callback function to queue

126

*/

127

function queuePostFlushCb(cb: SchedulerJob): void;

128

129

interface SchedulerJob {

130

(): void;

131

id?: number;

132

pre?: boolean;

133

active?: boolean;

134

computed?: boolean;

135

}

136

```

137

138

**Usage Examples:**

139

140

```typescript

141

import { ref, queuePostFlushCb } from "@vue/runtime-core";

142

143

const PostFlushExample = defineComponent({

144

setup() {

145

const list = ref([1, 2, 3]);

146

147

const addItems = () => {

148

// Update the list

149

list.value.push(4, 5, 6);

150

151

// Queue callback to run after all updates

152

queuePostFlushCb(() => {

153

console.log('All updates flushed, list length:', list.value.length);

154

// Safe to access updated DOM here

155

const listElement = document.querySelector('.list');

156

if (listElement) {

157

console.log('List DOM children:', listElement.children.length);

158

}

159

});

160

};

161

162

// Multiple post-flush callbacks

163

const multipleCallbacks = () => {

164

list.value = [10, 20, 30];

165

166

queuePostFlushCb(() => {

167

console.log('First post-flush callback');

168

});

169

170

queuePostFlushCb(() => {

171

console.log('Second post-flush callback');

172

});

173

};

174

175

// Callback with job properties

176

const advancedCallback = () => {

177

list.value.reverse();

178

179

const job: SchedulerJob = () => {

180

console.log('Advanced post-flush job executed');

181

};

182

job.id = 1;

183

184

queuePostFlushCb(job);

185

};

186

187

return {

188

list,

189

addItems,

190

multipleCallbacks,

191

advancedCallback

192

};

193

}

194

});

195

196

// Integration with watchers

197

const WatcherIntegration = defineComponent({

198

setup() {

199

const count = ref(0);

200

const computedDouble = computed(() => count.value * 2);

201

202

watch(count, (newCount) => {

203

console.log('Watcher fired for count:', newCount);

204

205

// Queue post-flush callback

206

queuePostFlushCb(() => {

207

console.log('Post-flush: computed value is', computedDouble.value);

208

});

209

});

210

211

return {

212

count,

213

computedDouble,

214

increment: () => count.value++

215

};

216

}

217

});

218

```

219

220

### Advanced Timing Patterns

221

222

Common patterns for precise timing control in Vue applications.

223

224

```typescript

225

// Debounced updates with nextTick

226

function useDebounceWithNextTick<T extends (...args: any[]) => any>(

227

fn: T,

228

delay: number

229

): T {

230

let timeoutId: NodeJS.Timeout;

231

232

return ((...args: Parameters<T>) => {

233

clearTimeout(timeoutId);

234

timeoutId = setTimeout(async () => {

235

await nextTick();

236

fn(...args);

237

}, delay);

238

}) as T;

239

}

240

241

// Batch DOM reads after updates

242

class DOMBatchReader {

243

private callbacks: (() => void)[] = [];

244

private scheduled = false;

245

246

read(callback: () => void) {

247

this.callbacks.push(callback);

248

if (!this.scheduled) {

249

this.scheduled = true;

250

nextTick(() => {

251

const toExecute = [...this.callbacks];

252

this.callbacks.length = 0;

253

this.scheduled = false;

254

255

toExecute.forEach(cb => cb());

256

});

257

}

258

}

259

}

260

261

const batchReader = new DOMBatchReader();

262

263

const BatchReadingExample = defineComponent({

264

setup() {

265

const height = ref(0);

266

const width = ref(0);

267

const elementRef = useTemplateRef<HTMLElement>('element');

268

269

const measureElement = () => {

270

batchReader.read(() => {

271

if (elementRef.value) {

272

height.value = elementRef.value.offsetHeight;

273

width.value = elementRef.value.offsetWidth;

274

}

275

});

276

};

277

278

return {

279

height,

280

width,

281

elementRef,

282

measureElement

283

};

284

}

285

});

286

287

// Coordinated updates across components

288

const useCoordinatedUpdate = () => {

289

const pendingUpdates = new Set<() => void>();

290

291

const scheduleUpdate = (updateFn: () => void) => {

292

pendingUpdates.add(updateFn);

293

294

nextTick(() => {

295

queuePostFlushCb(() => {

296

pendingUpdates.forEach(fn => fn());

297

pendingUpdates.clear();

298

});

299

});

300

};

301

302

return { scheduleUpdate };

303

};

304

305

// Animation frame coordination

306

const useAnimationTiming = () => {

307

const requestNextFrame = (callback: () => void) => {

308

nextTick(() => {

309

requestAnimationFrame(callback);

310

});

311

};

312

313

const requestDoubleFrame = (callback: () => void) => {

314

requestNextFrame(() => {

315

requestAnimationFrame(callback);

316

});

317

};

318

319

return {

320

requestNextFrame,

321

requestDoubleFrame

322

};

323

};

324

```

325

326

## Timing Guarantees

327

328

### nextTick Execution Order

329

330

1. **Synchronous updates**: All synchronous reactive updates are batched

331

2. **nextTick callbacks**: Queued callbacks execute after DOM updates

332

3. **Post-flush callbacks**: `queuePostFlushCb` callbacks execute after nextTick

333

4. **Browser paint**: Browser repaints after all Vue updates complete

334

335

### Best Practices

336

337

1. **DOM Measurements**: Always use `nextTick` before measuring DOM elements

338

2. **Third-party Integration**: Use `nextTick` when integrating with libraries that need updated DOM

339

3. **Animation Triggers**: Combine with `requestAnimationFrame` for smooth animations

340

4. **Error Boundaries**: Wrap DOM operations in try-catch after `nextTick`

341

342

### Performance Considerations

343

344

```typescript

345

// Good: Batch multiple updates

346

const batchUpdates = async () => {

347

item1.value = 'new value 1';

348

item2.value = 'new value 2';

349

item3.value = 'new value 3';

350

351

// Single nextTick for all updates

352

await nextTick();

353

354

measureAllElements();

355

};

356

357

// Avoid: Multiple nextTick calls for same update cycle

358

const inefficientUpdates = async () => {

359

item1.value = 'new value 1';

360

await nextTick(); // Unnecessary

361

362

item2.value = 'new value 2';

363

await nextTick(); // Unnecessary

364

365

item3.value = 'new value 3';

366

await nextTick(); // Only this one needed

367

};

368

```

369

370

## Types

371

372

```typescript { .api }

373

interface SchedulerJob {

374

(): void;

375

/**

376

* Job ID for sorting/deduplication

377

*/

378

id?: number;

379

/**

380

* Whether this is a pre-flush job

381

*/

382

pre?: boolean;

383

/**

384

* Whether the job is currently active

385

*/

386

active?: boolean;

387

/**

388

* Whether this job is from a computed

389

*/

390

computed?: boolean;

391

}

392

393

/**

394

* Function signature for nextTick

395

*/

396

interface NextTickFunction {

397

(): Promise<void>;

398

(fn: () => void): Promise<void>;

399

}

400

401

/**

402

* Function signature for queuePostFlushCb

403

*/

404

interface QueuePostFlushCbFunction {

405

(cb: SchedulerJob): void;

406

}

407

```