Truyền trực tuyến theo cách của bạn đến câu trả lời tức thì

Bất cứ ai đã sử dụng trình chạy dịch vụ đều có thể cho bạn biết rằng các trình chạy này không đồng bộ. Các coroutine chỉ dựa vào giao diện dựa trên sự kiện, chẳng hạn như FetchEvent và sử dụng lời hứa để báo hiệu khi các thao tác không đồng bộ hoàn tất.

Tính không đồng bộ cũng quan trọng không kém, mặc dù nhà phát triển ít thấy được điều này hơn, khi nói đến các phản hồi do trình xử lý sự kiện tìm nạp của worker dịch vụ cung cấp. Phản hồi truyền trực tuyến là tiêu chuẩn vàng ở đây: chúng cho phép trang đã tạo yêu cầu ban đầu bắt đầu xử lý phản hồi ngay khi có khối dữ liệu đầu tiên và có thể sử dụng các trình phân tích cú pháp được tối ưu hoá để truyền trực tuyến nhằm hiển thị nội dung một cách liên tục.

Khi viết trình xử lý sự kiện fetch của riêng mình, thông thường bạn chỉ cần truyền phương thức respondWith() một Response (hoặc một lời hứa cho Response) mà bạn nhận được thông qua fetch() hoặc caches.match(). Tin vui là các Response do cả hai phương thức đó tạo ra đều có thể truyền trực tuyến! Tin xấu là các Response được "tạo thủ công" được xây dựng không thể truyền trực tuyến, ít nhất là cho đến thời điểm hiện tại. Đó là lúc API Luồng xuất hiện.

Luồng?

Luồng là một nguồn dữ liệu có thể được tạo và thao tác tăng dần, đồng thời cung cấp giao diện để đọc hoặc ghi các đoạn dữ liệu không đồng bộ, trong đó chỉ có một tập hợp con có thể có trong bộ nhớ tại bất kỳ thời điểm nào. Hiện tại, chúng ta quan tâm đến ReadableStream. Bạn có thể dùng đối tượng này để tạo đối tượng Response được truyền đến fetchEvent.respondWith():

self.addEventListener('fetch', event => {
    var stream = new ReadableStream({
    start(controller) {
        if (/* there's more data */) {
        controller.enqueue(/* your data here */);
        } else {
        controller.close();
        }
    });
    });

    var response = new Response(stream, {
    headers: {'content-type': /* your content-type here */}
    });

    event.respondWith(response);
});

Trang có yêu cầu kích hoạt sự kiện fetch sẽ nhận được phản hồi truyền trực tuyến ngay khi event.respondWith() được gọi và sẽ tiếp tục đọc từ luồng đó miễn là trình chạy dịch vụ tiếp tục enqueue() dữ liệu bổ sung. Phản hồi từ trình chạy dịch vụ đến trang thực sự là không đồng bộ và chúng ta có toàn quyền kiểm soát việc điền luồng!

Trường hợp sử dụng thực tế

Có thể bạn đã nhận thấy ví dụ trước có một số nhận xét /* your data here */ phần giữ chỗ và không có nhiều thông tin chi tiết về cách triển khai thực tế. Vậy ví dụ thực tế sẽ như thế nào?

Jake Archibald (không có gì đáng ngạc nhiên!) có một ví dụ tuyệt vời về việc sử dụng luồng để kết hợp phản hồi HTML từ nhiều đoạn mã HTML được lưu vào bộ nhớ đệm, cùng với dữ liệu "trực tiếp" được truyền qua fetch() – trong trường hợp này là nội dung cho blog của anh

Ưu điểm của việc sử dụng phản hồi truyền trực tuyến, như Jake giải thích, là trình duyệt có thể phân tích cú pháp và hiển thị HTML khi nó truyền trực tuyến, bao gồm cả bit ban đầu được tải nhanh từ bộ nhớ đệm mà không cần phải đợi toàn bộ nội dung blog được tìm nạp xong. Điều này tận dụng tối đa khả năng kết xuất HTML tăng dần của trình duyệt. Các tài nguyên khác cũng có thể được kết xuất dần dần, chẳng hạn như một số định dạng hình ảnh và video, cũng có thể hưởng lợi từ phương pháp này.

Luồng? Hay vỏ ứng dụng?

Các phương pháp hay nhất hiện có về việc sử dụng trình chạy dịch vụ để hỗ trợ ứng dụng web tập trung vào mô hình Lớp phủ ứng dụng + nội dung động. Phương pháp đó dựa vào việc lưu vào bộ nhớ đệm một cách tích cực "vỏ" của ứng dụng web – HTML, JavaScript và CSS tối thiểu cần thiết để hiển thị cấu trúc và bố cục của bạn – sau đó tải nội dung động cần thiết cho từng trang cụ thể thông qua yêu cầu phía máy khách.

Luồng mang đến một giải pháp thay thế cho mô hình Vỏ ứng dụng, trong đó có một phản hồi HTML đầy đủ hơn được truyền đến trình duyệt khi người dùng chuyển đến một trang mới. Phản hồi được truyền trực tuyến có thể tận dụng các tài nguyên đã lưu vào bộ nhớ đệm, vì vậy, phản hồi này vẫn có thể cung cấp nhanh phần HTML ban đầu, ngay cả khi không có kết nối mạng! – nhưng cuối cùng, các phản hồi này trông giống như nội dung phản hồi truyền thống do máy chủ hiển thị. Ví dụ: nếu ứng dụng web của bạn được cung cấp bởi một hệ thống quản lý nội dung hiển thị HTML trên máy chủ bằng cách ghép nối một số mẫu, thì mô hình đó sẽ trực tiếp chuyển thành việc sử dụng phản hồi truyền trực tuyến, với logic tạo mẫu được sao chép trong trình chạy dịch vụ thay vì máy chủ của bạn. Như video sau đây minh hoạ, đối với trường hợp sử dụng đó, lợi thế về tốc độ mà các phản hồi được truyền trực tuyến mang lại có thể rất ấn tượng:

Một lợi thế quan trọng của việc truyền trực tuyến toàn bộ phản hồi HTML, giải thích lý do tại sao đây là phương án thay thế nhanh nhất trong video, là HTML được hiển thị trong yêu cầu điều hướng ban đầu có thể tận dụng tối đa trình phân tích cú pháp HTML trực tuyến của trình duyệt. Các đoạn HTML được chèn vào tài liệu sau khi trang tải xong (như thường thấy trong mô hình Vỏ ứng dụng) không thể tận dụng tính năng tối ưu hoá này.

Vì vậy, nếu đang ở giai đoạn lập kế hoạch triển khai worker dịch vụ, bạn nên sử dụng mô hình nào: phản hồi được truyền trực tuyến được hiển thị dần dần hoặc một lớp phủ nhẹ kết hợp với yêu cầu phía máy khách cho nội dung động? Không có gì ngạc nhiên khi câu trả lời là tuỳ thuộc vào việc bạn có phương thức triển khai hiện tại dựa trên CMS và một số mẫu hay không (lợi thế: luồng); liệu bạn có mong đợi các tải trọng HTML lớn, đơn lẻ sẽ được hưởng lợi từ tính năng kết xuất tăng dần hay không (lợi thế: luồng); liệu ứng dụng web của bạn có được mô hình hoá tốt nhất dưới dạng ứng dụng một trang hay không (lợi thế: Vỏ ứng dụng); và liệu bạn có cần một mô hình hiện được hỗ trợ trên nhiều bản phát hành ổn định của trình duyệt hay không (lợi thế: Vỏ ứng dụng).

Chúng ta vẫn đang ở giai đoạn đầu của các phản hồi phát trực tuyến do worker dịch vụ cung cấp. Chúng tôi mong muốn thấy các mô hình khác nhau phát triển và đặc biệt là thấy nhiều công cụ được phát triển hơn để tự động hoá các trường hợp sử dụng phổ biến.

Tìm hiểu sâu hơn về luồng

Nếu bạn đang tạo các luồng có thể đọc của riêng mình, thì việc chỉ gọi controller.enqueue() một cách không phân biệt có thể không đủ hoặc không hiệu quả. Jake trình bày một số chi tiết về cách sử dụng song song các phương thức start(), pull()cancel() để tạo luồng dữ liệu phù hợp với trường hợp sử dụng của bạn.

Đối với những người muốn biết thêm thông tin chi tiết, hãy tham khảo Thông số kỹ thuật của luồng.

Khả năng tương thích

Hỗ trợ việc tạo đối tượng Response bên trong worker dịch vụ bằng cách sử dụng ReadableStream làm nguồn của đối tượng đó đã được thêm vào Chrome 52.

Việc triển khai trình chạy dịch vụ của Firefox chưa hỗ trợ các phản hồi được ReadableStream hỗ trợ, nhưng có một lỗi theo dõi liên quan đến việc hỗ trợ API Luồng mà bạn có thể làm theo.

Bạn có thể theo dõi tiến trình hỗ trợ API Luồng không có tiền tố trong Edge, cùng với hỗ trợ trình chạy dịch vụ tổng thể tại Trang trạng thái nền tảng của Microsoft.