16 điểm bởi GN⁺ 2025-09-22 | Chưa có bình luận nào. | Chia sẻ qua WhatsApp
  • Trong JavaScript, setTimeout(0) thực tế thường không chạy ngay lập tức mà bị trễ tối thiểu 4ms, đây là giới hạn mặc định của trình duyệt để ngăn lạm dụng
  • Ràng buộc này nhằm ngăn website lạm dụng bộ hẹn giờ một cách bừa bãi gây hao pin hoặc làm giảm khả năng tương tác, và trong chế độ pin hay ở tab nền, giới hạn còn có thể bị siết mạnh hơn như 16ms hoặc 1 giây
  • Các nhà phát triển đã sử dụng nhiều API hẹn giờ thay thế như setImmediate, MessageChannel.postMessage, window.postMessage, scheduler.postTask để vượt qua giới hạn của setTimeout
  • Kết quả benchmark thực tế cho thấy Chrome và Firefox áp dụng mức chặn 4ms, nhưng MessageChannelscheduler.postTask hoạt động gần như không có độ trễ, còn Safari có xu hướng hạn chế setTimeout mạnh hơn
  • Về bản chất, đây là vấn đề cân bằng giữa bảo vệ trải nghiệm người dùng và quyền tự do của nhà phát triển; hiện tại Scheduler API đang trở thành lời giải được chuẩn hóa, nhưng nếu xảy ra lạm dụng thì cũng có thể sẽ xuất hiện can thiệp của trình duyệt (Intervention) mới

Bối cảnh của giới hạn setTimeout

  • Ngay cả setTimeout(0) cũng thường chỉ thực sự chạy sau tối thiểu 4ms do lạm dụng
    const start = performance.now()  
    setTimeout(() => {  
      // chạy sau khoảng 4ms  
      console.log(performance.now() - start)  
    }, 0)  
    
  • Mục đích là ngăn các lệnh gọi lặp vô tội vạ để giảm hao pin và độ trễ khi render
  • Một số trình duyệt còn tăng mức hạn chế tùy theo môi trường
    • Chế độ pin: Edge cũ là 16ms
    • Tab nền: Chrome có thể trì hoãn tới 1 giây

Sự xuất hiện của các API hẹn giờ khác

  • setImmediate: chỉ được hỗ trợ trên IE và Edge cũ, nay gần như đã biến mất
  • MessageChannel.postMessage: chuyển công việc vào event loop qua một kênh riêng
  • window.postMessage: hiệu năng tốt nhưng có nguy cơ xung đột với script khác
  • scheduler.postTask: được hỗ trợ trên trình duyệt hiện đại và được đánh giá là lựa chọn ổn định nhất

Kết quả benchmark (MacBook Pro 2021, đo lặp 101 lần)

  • Chrome 139: setTimeout 4.2ms, scheduler.postTask 0ms
  • Firefox 142: setTimeout 4.72ms, scheduler.postTask 0.01ms
  • Safari 18.4: setTimeout 26.73ms, MessageChannel 0.52ms, window.postMessage 0.05ms

Trường hợp của fake-indexeddb

  • IndexedDB muốn transaction được tự động commit ngay sau khi microtask của event loop kết thúc
  • setImmediate của Node.js là lý tưởng, nhưng trên trình duyệt thì setTimeout lại kém hiệu quả
  • Trên Chrome, một tác vụ mất 300ms có thể kéo dài tới 4.8 giây trên trình duyệt
  • Cách giải quyết là dùng mặc định scheduler.postTask, và dùng MessageChannel/window.postMessage làm phương án dự phòng để đảm bảo tương thích

Tranh luận về can thiệp của trình duyệt

  • Một phía cho rằng cần giới hạn timer để nhà phát triển được bảo vệ khỏi chính mình
  • Phía còn lại cho rằng cần đảm bảo tự do để nhà phát triển tự đo đạc và tối ưu
  • Cuối cùng, theo nguyên tắc ưu tiên người dùng, trình duyệt vẫn can thiệp (intervention) để ngăn lạm dụng
  • Scheduler API là sự thỏa hiệp giữa hai lập trường, được thiết kế để trao cho nhà phát triển quyền kiểm soát tác vụ chi tiết hơn đồng thời phù hợp với pipeline render của trình duyệt

Triển vọng sắp tới

  • postTaskpostMessage có vẻ sẽ tiếp tục không bị throttle trong một thời gian
  • Nhưng nếu các mức ưu tiên cao như user-blocking bị lạm dụng thì vẫn có thể lại xuất hiện can thiệp
  • Về dài hạn, thậm chí có thể sẽ cần một API thay thế khác như scheduler2

Chưa có bình luận nào.

Chưa có bình luận nào.