CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vue--runtime-core

Vue.js 3 runtime core library providing foundational APIs for building custom renderers and managing reactive component systems

Pending
Overview
Eval results
Files

scheduler-timing.mddocs/

Scheduler & Timing

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.

Capabilities

Next Tick Scheduling

Schedule callbacks to run after the next DOM update cycle.

/**
 * Defers callback execution until after the next DOM update cycle
 * @param fn - Optional callback function to execute
 * @returns Promise that resolves after the next tick
 */
function nextTick(fn?: () => void): Promise<void>;

Usage Examples:

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

const MyComponent = defineComponent({
  setup() {
    const count = ref(0);
    const elementRef = useTemplateRef<HTMLElement>('element');
    
    const updateAndRead = async () => {
      // Update reactive data
      count.value++;
      
      // Wait for DOM to update
      await nextTick();
      
      // Now DOM reflects the new count
      if (elementRef.value) {
        console.log('Updated DOM content:', elementRef.value.textContent);
        console.log('DOM height:', elementRef.value.offsetHeight);
      }
    };
    
    // With callback style
    const updateWithCallback = () => {
      count.value++;
      
      nextTick(() => {
        console.log('DOM updated with new count:', count.value);
      });
    };
    
    // Multiple nextTick calls
    const chainedUpdates = async () => {
      count.value = 1;
      await nextTick();
      console.log('First update complete');
      
      count.value = 2;
      await nextTick();
      console.log('Second update complete');
    };
    
    return {
      count,
      elementRef,
      updateAndRead,
      updateWithCallback,
      chainedUpdates
    };
  }
});

// Usage in lifecycle hooks
const LifecycleExample = defineComponent({
  setup() {
    const data = ref('initial');
    
    onMounted(async () => {
      // Change data
      data.value = 'mounted';
      
      // Wait for DOM update
      await nextTick();
      
      // Now safe to access updated DOM
      console.log('DOM updated after mount');
    });
    
    return { data };
  }
});

// Error handling with nextTick
const ErrorHandlingExample = defineComponent({
  setup() {
    const handleUpdate = async () => {
      try {
        // Some reactive updates
        // ...
        
        await nextTick();
        
        // DOM operations that might fail
        const element = document.querySelector('.my-element');
        if (element) {
          element.scrollIntoView();
        }
      } catch (error) {
        console.error('Error after nextTick:', error);
      }
    };
    
    return { handleUpdate };
  }
});

Post-flush Callback Queue

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

/**
 * Queues a callback to run after all pending updates have been flushed
 * @param cb - Callback function to queue
 */
function queuePostFlushCb(cb: SchedulerJob): void;

interface SchedulerJob {
  (): void;
  id?: number;
  pre?: boolean;
  active?: boolean;
  computed?: boolean;
}

Usage Examples:

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

const PostFlushExample = defineComponent({
  setup() {
    const list = ref([1, 2, 3]);
    
    const addItems = () => {
      // Update the list
      list.value.push(4, 5, 6);
      
      // Queue callback to run after all updates
      queuePostFlushCb(() => {
        console.log('All updates flushed, list length:', list.value.length);
        // Safe to access updated DOM here
        const listElement = document.querySelector('.list');
        if (listElement) {
          console.log('List DOM children:', listElement.children.length);
        }
      });
    };
    
    // Multiple post-flush callbacks
    const multipleCallbacks = () => {
      list.value = [10, 20, 30];
      
      queuePostFlushCb(() => {
        console.log('First post-flush callback');
      });
      
      queuePostFlushCb(() => {
        console.log('Second post-flush callback');
      });
    };
    
    // Callback with job properties
    const advancedCallback = () => {
      list.value.reverse();
      
      const job: SchedulerJob = () => {
        console.log('Advanced post-flush job executed');
      };
      job.id = 1;
      
      queuePostFlushCb(job);
    };
    
    return {
      list,
      addItems,
      multipleCallbacks,
      advancedCallback
    };
  }
});

// Integration with watchers
const WatcherIntegration = defineComponent({
  setup() {
    const count = ref(0);
    const computedDouble = computed(() => count.value * 2);
    
    watch(count, (newCount) => {
      console.log('Watcher fired for count:', newCount);
      
      // Queue post-flush callback
      queuePostFlushCb(() => {
        console.log('Post-flush: computed value is', computedDouble.value);
      });
    });
    
    return {
      count,
      computedDouble,
      increment: () => count.value++
    };
  }
});

Advanced Timing Patterns

Common patterns for precise timing control in Vue applications.

// Debounced updates with nextTick
function useDebounceWithNextTick<T extends (...args: any[]) => any>(
  fn: T,
  delay: number
): T {
  let timeoutId: NodeJS.Timeout;
  
  return ((...args: Parameters<T>) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(async () => {
      await nextTick();
      fn(...args);
    }, delay);
  }) as T;
}

// Batch DOM reads after updates
class DOMBatchReader {
  private callbacks: (() => void)[] = [];
  private scheduled = false;
  
  read(callback: () => void) {
    this.callbacks.push(callback);
    if (!this.scheduled) {
      this.scheduled = true;
      nextTick(() => {
        const toExecute = [...this.callbacks];
        this.callbacks.length = 0;
        this.scheduled = false;
        
        toExecute.forEach(cb => cb());
      });
    }
  }
}

const batchReader = new DOMBatchReader();

const BatchReadingExample = defineComponent({
  setup() {
    const height = ref(0);
    const width = ref(0);
    const elementRef = useTemplateRef<HTMLElement>('element');
    
    const measureElement = () => {
      batchReader.read(() => {
        if (elementRef.value) {
          height.value = elementRef.value.offsetHeight;
          width.value = elementRef.value.offsetWidth;
        }
      });
    };
    
    return {
      height,
      width,
      elementRef,
      measureElement
    };
  }
});

// Coordinated updates across components
const useCoordinatedUpdate = () => {
  const pendingUpdates = new Set<() => void>();
  
  const scheduleUpdate = (updateFn: () => void) => {
    pendingUpdates.add(updateFn);
    
    nextTick(() => {
      queuePostFlushCb(() => {
        pendingUpdates.forEach(fn => fn());
        pendingUpdates.clear();
      });
    });
  };
  
  return { scheduleUpdate };
};

// Animation frame coordination
const useAnimationTiming = () => {
  const requestNextFrame = (callback: () => void) => {
    nextTick(() => {
      requestAnimationFrame(callback);
    });
  };
  
  const requestDoubleFrame = (callback: () => void) => {
    requestNextFrame(() => {
      requestAnimationFrame(callback);
    });
  };
  
  return {
    requestNextFrame,
    requestDoubleFrame
  };
};

Timing Guarantees

nextTick Execution Order

  1. Synchronous updates: All synchronous reactive updates are batched
  2. nextTick callbacks: Queued callbacks execute after DOM updates
  3. Post-flush callbacks: queuePostFlushCb callbacks execute after nextTick
  4. Browser paint: Browser repaints after all Vue updates complete

Best Practices

  1. DOM Measurements: Always use nextTick before measuring DOM elements
  2. Third-party Integration: Use nextTick when integrating with libraries that need updated DOM
  3. Animation Triggers: Combine with requestAnimationFrame for smooth animations
  4. Error Boundaries: Wrap DOM operations in try-catch after nextTick

Performance Considerations

// Good: Batch multiple updates
const batchUpdates = async () => {
  item1.value = 'new value 1';
  item2.value = 'new value 2';
  item3.value = 'new value 3';
  
  // Single nextTick for all updates
  await nextTick();
  
  measureAllElements();
};

// Avoid: Multiple nextTick calls for same update cycle
const inefficientUpdates = async () => {
  item1.value = 'new value 1';
  await nextTick(); // Unnecessary
  
  item2.value = 'new value 2';  
  await nextTick(); // Unnecessary
  
  item3.value = 'new value 3';
  await nextTick(); // Only this one needed
};

Types

interface SchedulerJob {
  (): void;
  /**
   * Job ID for sorting/deduplication
   */
  id?: number;
  /**
   * Whether this is a pre-flush job
   */
  pre?: boolean;
  /**
   * Whether the job is currently active
   */
  active?: boolean;
  /**
   * Whether this job is from a computed
   */
  computed?: boolean;
}

/**
 * Function signature for nextTick
 */
interface NextTickFunction {
  (): Promise<void>;
  (fn: () => void): Promise<void>;
}

/**
 * Function signature for queuePostFlushCb
 */
interface QueuePostFlushCbFunction {
  (cb: SchedulerJob): void;
}

Install with Tessl CLI

npx tessl i tessl/npm-vue--runtime-core

docs

asset-resolution.md

builtin-components.md

components.md

composition-helpers.md

dependency-injection.md

error-handling.md

hydration.md

index.md

internal-render-helpers.md

lifecycle.md

reactivity.md

scheduler-timing.md

ssr-context.md

vdom-rendering.md

watch-effects.md

tile.json