Tối ưu hoá độ trễ khi nhập

Tìm hiểu xem độ trễ đầu vào là gì và tìm hiểu các kỹ thuật giảm độ trễ để tương tác nhanh hơn.

Tương tác trên web là những điều phức tạp, với tất cả các loại hoạt động xảy ra trong trình duyệt để thúc đẩy chúng. Tuy nhiên, điểm chung của tất cả các thuật toán này là phải chịu một số độ trễ đầu vào trước khi các lệnh gọi lại sự kiện bắt đầu chạy. Trong hướng dẫn này, bạn sẽ tìm hiểu độ trễ đầu vào là gì và những việc bạn có thể làm để giảm thiểu độ trễ để các tương tác trên trang web chạy nhanh hơn.

Độ trễ khi nhập dữ liệu là gì?

Độ trễ đầu vào là khoảng thời gian tính từ thời điểm người dùng tương tác lần đầu với một trang (chẳng hạn như nhấn vào màn hình, nhấp bằng chuột hoặc nhấn phím) cho đến khi lệnh gọi lại sự kiện cho lượt tương tác bắt đầu chạy. Mỗi tương tác bắt đầu với độ trễ đầu vào nhất định.

Hình ảnh trực quan được đơn giản hoá về độ trễ đầu vào. Ở bên trái là hình vẽ con trỏ chuột với hình ngôi sao phía sau, biểu thị sự bắt đầu của một tương tác. Bên phải là hình vẽ bánh răng, biểu thị thời điểm các trình xử lý sự kiện cho một tương tác bắt đầu chạy. Khoảng cách ở giữa được ghi chú là độ trễ đầu vào bằng dấu ngoặc nhọn.
Cơ chế gây ra độ trễ đầu vào. Khi hệ điều hành nhận được dữ liệu đầu vào, dữ liệu đó phải được chuyển đến trình duyệt trước khi quá trình tương tác bắt đầu. Quá trình này sẽ mất một thời gian nhất định và có thể tăng lên nếu luồng chính hiện có.

Không thể tránh khỏi một số phần độ trễ đầu vào: hệ điều hành luôn mất một khoảng thời gian nhất định để nhận ra sự kiện đầu vào và truyền sự kiện đó đến trình duyệt. Tuy nhiên, phần độ trễ đầu vào đó thường thậm chí không đáng kể và có những điều khác tự xảy ra trên trang có thể khiến độ trễ đầu vào đủ lâu để gây ra sự cố.

Cách suy nghĩ về độ trễ đầu vào

Nói chung, bạn nên giữ mọi phần của một tương tác càng ngắn càng tốt để trang web của bạn có cơ hội tốt nhất đáp ứng chỉ số Tương tác với Hiển thị tiếp theo (INP) "tốt" , bất kể thiết bị của người dùng là gì. Việc kiểm tra độ trễ đầu vào chỉ là một phần trong quá trình đạt được ngưỡng đó.

Do đó, bạn nên nhắm đến độ trễ đầu vào ngắn nhất có thể để đáp ứng "tốt" của INP ngưỡng. Tuy nhiên, bạn nên lưu ý rằng bạn không thể loại bỏ hoàn toàn độ trễ đầu vào. Miễn là bạn tránh hoạt động quá mức trên luồng chính trong khi người dùng đang cố gắng tương tác với trang của bạn, thì độ trễ nhập dữ liệu phải đủ thấp để tránh sự cố.

Cách giảm thiểu độ trễ nhập

Như đã đề cập trước đó, không thể tránh khỏi một số độ trễ đầu vào, nhưng mặt khác, thể tránh được độ trễ đầu vào. Sau đây là một số điều cần xem xét nếu bạn đang gặp khó khăn với tình trạng chậm trễ đầu vào trong thời gian dài.

Tránh việc hẹn giờ lặp lại, làm khởi động quá trình hoạt động quá mức trên luồng chính

Có 2 hàm bộ tính giờ thường dùng trong JavaScript có thể góp phần gây ra độ trễ đầu vào: setTimeoutsetInterval. Sự khác biệt giữa 2 cách này là setTimeout lên lịch để chạy lệnh gọi lại sau một khoảng thời gian cụ thể. Mặt khác, setInterval lên lịch cho lệnh gọi lại chạy n mili giây vĩnh viễn hoặc cho đến khi bộ tính giờ dừng bằng clearInterval.

Bản thân setTimeout không có vấn đề gì, trên thực tế, nó có thể hữu ích trong việc tránh các tác vụ dài. Tuy nhiên, điều này phụ thuộc vào thời điểm hết thời gian chờ và việc người dùng có cố gắng tương tác với trang khi lệnh gọi lại hết thời gian chờ chạy hay không.

Ngoài ra, setTimeout có thể chạy trong một vòng lặp hoặc đệ quy, trong đó hoạt động giống setInterval hơn, mặc dù không nên lên lịch vòng lặp tiếp theo cho đến khi bước trước đó hoàn tất. Mặc dù điều này có nghĩa là vòng lặp sẽ tạo ra luồng chính mỗi khi setTimeout được gọi, nhưng bạn nên chú ý để đảm bảo lệnh gọi lại của luồng này không làm quá nhiều việc.

setInterval chạy lệnh gọi lại trong một khoảng thời gian, do đó có nhiều khả năng cản trở hoạt động tương tác. Nguyên nhân là do – không giống như một thực thể đơn lẻ của lệnh gọi setTimeout (là lệnh gọi lại một lần có thể cản trở quá trình tương tác của người dùng), bản chất định kỳ của setInterval khiến khả năng cao hơn sẽ cản trở một lượt tương tác, từ đó làm tăng độ trễ đầu vào của hoạt động tương tác.

Ảnh chụp màn hình trình phân tích hiệu suất trong Công cụ của Chrome cho nhà phát triển minh hoạ độ trễ đầu vào. Một tác vụ được hàm bộ tính giờ kích hoạt sẽ xảy ra ngay trước khi người dùng bắt đầu một lượt tương tác nhấp chuột. Tuy nhiên, bộ tính giờ kéo dài độ trễ đầu vào, khiến các lệnh gọi lại sự kiện của hoạt động tương tác chạy muộn hơn so với bình thường.
Một bộ tính giờ do lệnh gọi setInterval trước đó đăng ký, góp phần gây ra độ trễ đầu vào như mô tả trong bảng điều khiển hiệu suất của Chrome Công cụ cho nhà phát triển. Độ trễ đầu vào tăng thêm khiến các lệnh gọi lại sự kiện cho hoạt động tương tác chạy muộn hơn so với bình thường.

Nếu bộ tính giờ xuất hiện trong mã của bên thứ nhất, thì bạn có quyền kiểm soát chúng. Hãy đánh giá xem bạn có cần chúng không hoặc cố gắng hết sức để giảm lượng công việc trong đó nhiều nhất có thể. Tuy nhiên, đồng hồ đếm ngược trong tập lệnh của bên thứ ba lại khác. Bạn thường không kiểm soát được hoạt động của tập lệnh bên thứ ba. Việc khắc phục vấn đề về hiệu suất trong mã của bên thứ ba thường bao gồm việc làm việc với các bên liên quan để xác định xem tập lệnh nhất định của bên thứ ba có cần thiết hay không. Nếu có, hãy liên hệ với nhà cung cấp tập lệnh của bên thứ ba để xác định những việc có thể làm nhằm khắc phục các vấn đề về hiệu suất mà họ có thể gây ra trên trang web của bạn.

Tránh các tác vụ dài

Một cách để giảm thiểu độ trễ đầu vào dài là tránh các tác vụ dài. Khi bạn có quá nhiều thao tác trên luồng chính chặn luồng chính trong quá trình tương tác, điều này sẽ làm tăng độ trễ đầu vào cho chúng trước khi các tác vụ dài có cơ hội kết thúc.

Hình ảnh minh hoạ khoảng thời gian các tác vụ kéo dài độ trễ nhập. Ở trên cùng, một lượt tương tác xảy ra ngay sau khi một tác vụ dài chạy, gây ra độ trễ đầu vào đáng kể khiến các lệnh gọi lại sự kiện chạy muộn hơn nhiều so với dự kiến. Ở phía dưới cùng, một lượt tương tác xảy ra gần như cùng lúc, nhưng tác vụ dài được chia thành nhiều phần nhỏ hơn nhờ mang lại, cho phép lệnh gọi lại sự kiện của hoạt động tương tác chạy sớm hơn nhiều.
Hình ảnh trực quan về những gì sẽ xảy ra với tương tác khi các tác vụ quá dài và trình duyệt không thể phản hồi đủ nhanh với các tương tác, so với khi các tác vụ dài hơn được chia thành các tác vụ nhỏ hơn.

Bên cạnh việc giảm thiểu khối lượng công việc bạn phải làm trong một nhiệm vụ, và bạn nên luôn luôn cố gắng thực hiện ít công việc nhất có thể trên luồng chính, bạn có thể cải thiện khả năng phản hồi với hoạt động đầu vào của người dùng bằng cách chia nhỏ các nhiệm vụ dài.

Chú ý đến tình trạng trùng lặp tương tác

Một phần đặc biệt khó khăn trong việc tối ưu hoá INP là nếu bạn có các lượt tương tác trùng lặp. Chồng chéo tương tác có nghĩa là sau khi đã tương tác với một phần tử, bạn thực hiện một tương tác khác với trang trước khi tương tác ban đầu có cơ hội hiển thị khung tiếp theo.

Mô tả thời điểm các nhiệm vụ có thể chồng chéo nhau dẫn đến độ trễ đầu vào dài. Trong mô tả này, một lượt tương tác nhấp trùng lặp với một lượt tương tác nhấn phím để tăng độ trễ đầu vào cho lượt tương tác nhấn phím.
Hình ảnh trực quan về hai hoạt động tương tác đồng thời trong trình phân tích hiệu suất trong Công cụ cho nhà phát triển của Chrome. Quá trình kết xuất trong lượt tương tác nhấp ban đầu gây ra độ trễ đầu vào cho lượt tương tác tiếp theo với bàn phím.

Các nguồn tương tác trùng lặp có thể chỉ đơn giản như việc người dùng thực hiện nhiều tương tác trong một khoảng thời gian ngắn. Điều này có thể xảy ra khi người dùng nhập vào các trường biểu mẫu, nơi nhiều hoạt động tương tác với bàn phím có thể xảy ra trong một khoảng thời gian rất ngắn. Nếu công việc trên một sự kiện chính đặc biệt tốn kém (chẳng hạn như trong trường hợp phổ biến là các trường tự động hoàn thành, trong đó yêu cầu mạng được thực hiện qua hệ thống phụ trợ), thì bạn có một số lựa chọn như sau:

  • Cân nhắc huỷ bỏ dữ liệu đầu vào để giới hạn số lần một lệnh gọi lại sự kiện thực thi trong một khoảng thời gian nhất định.
  • Sử dụng AbortController để huỷ các yêu cầu fetch đi để luồng chính không bị nghẽn khi xử lý các lệnh gọi lại fetch. Lưu ý: Bạn cũng có thể dùng thuộc tính signal của thực thể AbortController để huỷ sự kiện.

Một nguồn khác làm tăng độ trễ đầu vào do các tương tác trùng lặp có thể là các ảnh động tốn kém. Cụ thể, các ảnh động trong JavaScript có thể kích hoạt nhiều lệnh gọi requestAnimationFrame, gây cản trở các hoạt động tương tác của người dùng. Để giải quyết vấn đề này, hãy sử dụng ảnh động CSS bất cứ khi nào có thể để tránh xếp hàng các khung ảnh động có khả năng tốn kém. Tuy nhiên, nếu bạn làm như vậy, hãy đảm bảo bạn tránh ảnh động không được kết hợp để ảnh động chạy chủ yếu trên GPU và các luồng trình tổng hợp chứ không chạy trên luồng chính.

Kết luận

Mặc dù độ trễ đầu vào có thể không chiếm phần lớn thời gian cần để chạy các lượt tương tác, nhưng bạn cần hiểu rằng mỗi phần của một lượt tương tác đều cần một khoảng thời gian để giảm thiểu thời gian tương tác. Nếu quan sát thấy độ trễ đầu vào lâu, thì bạn có cơ hội giảm độ trễ này. Việc tránh các lệnh gọi lại bộ tính giờ lặp lại, chia nhỏ các tác vụ dài và lưu ý đến khả năng trùng lặp tương tác đều có thể giúp bạn giảm độ trễ đầu vào, dẫn đến khả năng tương tác nhanh hơn cho người dùng trên trang web của bạn.

Hình ảnh chính trong Unsplash của Erik Mclean.