6 điểm bởi GN⁺ 2025-09-23 | 1 bình luận | Chia sẻ qua WhatsApp
  • Cap'n Webgiao thức RPC mới được triển khai bằng TypeScript, được tối ưu cho môi trường web và hoạt động trên nhiều runtime JavaScript khác nhau
  • Không cần schema hay boilerplate rườm rà, cung cấp tuần tự hóa dựa trên JSONđịnh dạng dữ liệu con người có thể đọc được
  • Thông qua mô hình dựa trên object-capability, nó hỗ trợ gọi hai chiều, truyền tham chiếu hàm và đối tượng, promise pipelining, và triển khai các mẫu bảo mật
  • Hỗ trợ nhiều môi trường mạng như WebSocket, HTTP, postMessage, đồng thời là mã nguồn mở nhẹ dưới 10kB
  • Không chỉ giải quyết vấn đề waterfall tương tự GraphQL, mà còn cho phép mô hình hóa RPC tự nhiên như API JavaScript thông thường

Cap'n Web là gì

  • Cap'n Webhệ thống RPC (protocol) mã nguồn mở dựa trên TypeScript do Cloudflare phát triển
  • Lấy cảm hứng từ Cap'n Proto, nhưng hoạt động không cần định nghĩa schema riêng, và áp dụng cách tuần tự hóa thân thiện với con người bằng JSON
  • Tích hợp với TypeScript để cải thiện trải nghiệm lập trình viên như tự động hoàn thành và kiểm tra kiểu; việc xác thực kiểu ở runtime có thể xử lý riêng (như type guard)
  • Hỗ trợ các giao thức mạng như HTTP, WebSocket, postMessage và chạy trên các trình duyệt chính, Cloudflare Workers, Node.js
  • Cấu trúc nhẹ không phụ thuộc thư viện, có kích thước dưới 10kB sau khi minify + gzip

Mô hình object-capability (OCap) của Cap'n Web

  • Áp dụng mô hình dựa trên object-capability, cho phép biểu đạt phong phú hơn các hệ thống RPC hiện có
    • Gọi hai chiều: client và server có thể gọi hàm của nhau
    • Truyền tham chiếu hàm và đối tượng: khi truyền hàm hoặc đối tượng qua RPC, phía nhận sẽ nhận stub và khi gọi thì thực thi tại vị trí gốc
    • Promise Pipelining: khi nối chuỗi nhiều RPC, có thể xử lý chỉ với một lần round trip mạng
    • Mẫu bảo mật: có thể triển khai tự nhiên các cơ chế kiểm soát bảo mật như cấp quyền và quản lý phiên

Cách sử dụng cơ bản

  • Ví dụ phía client

    import { newWebSocketRpcSession } from "capnweb"  
    let api = newWebSocketRpcSession("wss://example.com/api")  
    let result = await api.hello("World")  
    console.log(result)  
    
  • Ví dụ phía server (dựa trên Cloudflare Worker)

    import { RpcTarget, newWorkersRpcResponse } from "capnweb"  
    class MyApiServer extends RpcTarget {  
      hello(name) {  
        return `Hello, ${name}!`  
      }  
    }  
    export default {  
      fetch(request, env, ctx) {  
        let url = new URL(request.url)  
        if (url.pathname === "/api") {  
          return newWorkersRpcResponse(request, new MyApiServer())  
        }  
        return new Response("Not found", {status: 404})  
      }  
    }  
    
    Quảng cáo
  • Có thể dễ dàng thêm method vào API, truyền callback từ client, cũng như định nghĩa và áp dụng interface TypeScript

RPC là gì và đặc điểm trong Cap'n Web

  • RPC (Remote Procedure Call) là khái niệm cho phép hai chương trình trên mạng giao tiếp như thể đang gọi hàm
  • Khác với giao thức HTTP/REST truyền thống, RPC cung cấp lớp trừu tượng gọi hàm, cho phép viết mã phù hợp với cách tư duy của lập trình viên
  • Cap'n Web phù hợp với luồng JavaScript hiện đại nhờ hỗ trợ async/await, Promise, Exception
  • Không giống những tranh cãi lịch sử về RPC (gọi đồng bộ, lỗi mạng), trong môi trường JS hiện đại nó có thể được dùng an toàn và hiệu quả hơn

Các kịch bản sử dụng Cap'n Web

  • Phù hợp với mọi môi trường cần giao tiếp mạng giữa hai ứng dụng JavaScript
    • Gọi giữa client-server, giữa các microservice, v.v.
    • Đặc biệt phù hợp với web app cộng tác thời gian thực và các tương tác vượt qua ranh giới bảo mật phức tạp
  • Vẫn đang ở giai đoạn thử nghiệm, nên càng hữu ích hơn với các nhà phát triển cởi mở với công nghệ mới

Nhiều tính năng khác nhau

Chế độ batch HTTP

  • Khi không cần kết nối liên tục, có thể dùng chế độ batch HTTP để gộp nhiều lệnh gọi RPC và xử lý cùng lúc

    import { newHttpBatchRpcSession } from "capnweb"  
    let batch = newHttpBatchRpcSession("https://example.com/api";)  
    let result = await batch.hello("World")  
    console.log(result)  
    
    Quảng cáo
  • Có thể thực thi đồng thời nhiều lời gọi trong cùng một batch và nhận kết quả song song

    let promise1 = batch.hello("Alice")  
    let promise2 = batch.hello("Bob")  
    let [result1, result2] = await Promise.all([promise1, promise2])  
    

Promise Pipelining (gọi chuỗi)

  • Hỗ trợ cách dùng ngay kết quả làm đối số cho lệnh gọi tiếp theo mà không cần chờ lệnh gọi trước hoàn tất

  • Ví dụ: truyền trực tiếp Promise kết quả của getMyName() vào hello() để xử lý chỉ với một lần round trip mạng

    let namePromise = batch.getMyName()  
    let result = await batch.hello(namePromise)  
    
  • Promise của Cap'n Web hoạt động như đối tượng proxy, cho phép nối thêm method mà không bị trì hoãn

    let sessionPromise = batch.authenticate(apiKey)  
    let name = await sessionPromise.whoami()  
    
    Quảng cáo

Bảo mật: xác thực và object-capability

  • Thông qua method authenticate, hệ thống cấp đối tượng quyền hạn (session) khi thành công, sau đó có thể gọi chức năng mà không cần thêm bước xác thực
  • Khác với RPC truyền thống, không thể giả mạo đối tượng session, và cũng không thể truy cập method yêu cầu quyền nếu chưa xác thực
  • Tự nhiên khắc phục các giới hạn cấu trúc của WebSocket, đồng thời bảo đảm tính nhất quán của logic xác thực
  • Khi khai báo interface API bằng TypeScript, có thể tự động áp dụng từ client đến server, đồng thời bảo đảm tự động hoàn thành và độ an toàn kiểu

So sánh với GraphQL và điểm khác biệt của Cap'n Web

  • GraphQL làm giảm vấn đề waterfall nhiều tầng của REST, nhưng đòi hỏi đưa vào ngôn ngữ, schema và toolchain mới

  • Cap'n Web giải quyết vấn đề waterfall chỉ bằng mã JavaScript, đồng thời

    • nhờ hỗ trợ promise pipelining/tham chiếu đối tượng, có thể mô hình hóa tự nhiên các lời gọi lồng nhau hoặc logic giao dịch phức hợp
    let user = api.createUser({ name: "Alice" })  
    let friendRequest = await user.sendFriendRequest("Bob")  
    
  • Có thể sử dụng giống API JavaScript mà không cần gánh độ phức tạp và chi phí học tập/quản lý của GraphQL

    Quảng cáo

Phép toán mảng (array.map v.v.) và tối ưu hóa

  • Trong Cap'n Web, có thể thực hiện phép map trên từng phần tử mảng mà không phát sinh thêm round trip mạng

  • Hàm callback của map được chạy một lần ở client để ghi lại nội dung tính toán (record-replay), sau đó gửi lên server để xử lý hàng loạt phía server

    let friendsWithPhotos = friendsPromise.map(friend => {  
      return {friend, photo: api.getUserPhoto(friend.id)}  
    })  
    let results = await friendsWithPhotos  
    
  • Thông qua một DSL chuyên biệt trong phạm vi giới hạn, nó cho phép biểu đạt như hàm JavaScript, nhưng thực tế dùng giao thức Cap'n Web để tối ưu nhiều lời gọi

Cấu trúc giao thức nội bộ và luồng giao tiếp

  • Truyền dữ liệu có cấu trúc thông qua JSON + tiền xử lý đặc biệt, hỗ trợ các kiểu đặc biệt như mảng, ngày tháng
  • Giao thức đối xứng cho phép giao tiếp hai chiều không phân biệt client và server
  • Mỗi bên (ví dụ: Alice và Bob) quản lý bảng export/import, và phân biệt tham chiếu đối tượng/hàm bằng ID
  • Thông qua các thông điệp push/pull và việc cấp phát Promise ID, có thể phản ánh nhiều lời gọi trong một round trip

Tình hình hiện tại và các trường hợp áp dụng

  • Cap'n Web hiện vẫn là mã nguồn mở mang tính thử nghiệm, nhưng đã được dùng trong dịch vụ thực tế như remote bindings của Cloudflare Wrangler
  • Dự kiến sẽ có thêm các bài blog và nhiều thử nghiệm frontend khác
  • Được phát hành theo giấy phép MIT, bất kỳ ai cũng có thể tự do áp dụng
  • Đi tới kho GitHub

1 bình luận

 
GN⁺ 2025-09-23
Ý kiến trên Hacker News
  • Tôi có hai điều thắc mắc

    1. Tôi muốn biết cách triển khai các bản cập nhật ứng dụng khi ngữ nghĩa RPC thay đổi. Nói cách khác, làm sao có thể đảm bảo client và server đang dùng cùng một phiên bản RPC? Protocol Buffers (grpc/avro v.v.) cố gắng giải quyết trực tiếp vấn đề này
    2. Tôi muốn biết nên xử lý kết nối mạng không ổn định như thế nào. Bảng export/import dường như gắn trực tiếp với kết nối WebSocket có trạng thái, nên tôi nghĩ nếu kết nối bị ngắt thì sẽ mất trạng thái. Về lý thuyết, client/server có thể cache trạng thái và khôi phục khi kết nối lại, nhưng bảng có thể chứa closure nên sẽ khó tuần tự hóa và có thể phát sinh vấn đề bộ nhớ. Tôi muốn biết nhóm đã cân nhắc chuyện này như thế nào
      Tôi nghĩ đây là một công trình thật sự mang tính đột phá
      1. Có thể xem nó giống như việc cập nhật API JavaScript mà không làm hỏng các caller hiện có. Chỉ cần tuân thủ những nguyên tắc tương thích cơ bản như với lời gọi hàm cục bộ, thì vẫn có thể thêm method mới, tham số tùy chọn, v.v.
      2. Khi kết nối bị ngắt, bạn phải kết nối lại và dựng lại đối tượng từ đầu. Trong một ứng dụng React thực tế, stub RPC chính được truyền vào component cấp cao nhất làm tham số. Component này tạo nhiều đối tượng con và truyền chúng xuống cho các component con. Khi kết nối bị ngắt, ta tạo một stub mới rồi truyền lại vào component cấp cao nhất. Khi đó sẽ re-render như mọi thay đổi state khác, và tất cả component con sẽ fetch lại các đối tượng con cần thiết
        Nếu có đối tượng subscription dùng callback, bạn nên thiết kế API sao cho khi bắt đầu có thể chỉ định “tin nhắn đã thấy gần nhất”. Như vậy có thể tiếp tục nhận dữ liệu ngay từ đó mà không bị bỏ lỡ đoạn nào ở giữa
        Có lẽ nên viết một loạt bài blog để tổng hợp các mẫu thiết kế như thế này
  • Phần nói về cách họ giải quyết vấn đề mảng thực sự rất thú vị nhưng đồng thời cũng hơi đáng sợ liên kết blog
    Với .map(), họ không gửi trực tiếp mã JavaScript lên server, nhưng lại gửi một thứ giống như “mã”, bằng cách dùng một DSL miền hẹp. Ở phía client, callback được chạy thử một lần với giá trị placeholder, rồi hành vi của nó được theo dõi theo kiểu record-replay để gửi một instruction set lên server. Ở server, các instruction đó được nhận và thực thi cho từng phần tử của mảng.
    Tức là nhà phát triển chỉ dùng method JS bình thường, nhưng bên dưới thực chất có một mẹo biến nó sang một DSL hẹp. Callback chỉ được hoạt động đồng bộ, không thể await. Thay vào đó chỉ cho phép promise pipelining, để bắt toàn bộ quá trình và gửi sang server, nơi nó sẽ được chạy lại mỗi khi cần

    • C# có expression tree để xử lý kiểu vấn đề này. Entity Framework dùng nó khi nhận lambda rồi chuyển thành truy vấn SQL. Tức là có thể dùng bằng cách quét hoặc biến đổi mã mà không cần thực thi mã đó
      Ví dụ, db.People.Where(p => p.Name == "Joe") không phải là Where nhận một predicate function thực sự, mà là nhận expression, nên nó có thể quét đoạn mã được truyền vào để kiểm tra trường Name có khớp với "Joe" hay không rồi chuyển thành mệnh đề SQL WHERE
      JavaScript không có cơ chế như vậy, nên họ phải bắt chước bằng cách đưa giá trị placeholder vào và ghi lại từng bước nó hoạt động ra sao

    • Gần đây khi làm DSL truy vấn cho Tanstack DB, họ cũng dùng mẹo record-replay này liên kết hướng dẫn. Họ truyền đối tượng RefProxy vào các callback where/select/join rồi theo dõi các prop/phép toán nào xảy ra trên đối tượng đó.
      Trong JS, không thể chặn trực tiếp các toán tử thông thường (==, > v.v.), nên họ tạo các hàm nhỏ có thể trace như eq/gt/not v.v., chạy callback đúng một lần để bắt biểu thức được liên kết rồi biến nó thành IR
      Điều thú vị là họ còn trace được cả toán tử spread của JS
      Kenton, tôi tự hỏi liệu khái niệm này có thể được thêm vào capnweb dưới dạng fake operator (eq, gt, in v.v.) để có tính năng tracing từ xa hay không

    • Có vẻ như câu lệnh điều kiện bị cấm (giống quy tắc hook của React), nên tôi thắc mắc họ thực thi kiểu ràng buộc đó như thế nào

  • Dự án này rất thú vị
    Nó có nhiều điểm giống với các thư viện compiler cho ML (TensorFlow 1, JAX jit, PyTorch compile v.v.). Chúng tạo operation graph bằng tracing, rồi compile hoặc chuyển đổi nó để thực thi phù hợp với VM
    Hiện nay, ngôn ngữ động được dùng làm frontend để định nghĩa thứ gì đó mà không cần tạo DSL mới, thay vào đó giấu phần biến đổi AST vào ngay trong ngôn ngữ script sẵn có
    Trong ML, ta trì hoãn việc chạy kernel GPU/linalg để gộp nhiều kernel lại; còn với RPC như Cap'n Web, có thể trì hoãn các yêu cầu mạng để gộp nhiều network call lại với nhau
    Cuối cùng, điểm cốt lõi là tách instruction plane và data plane; ngay cả một CPU đơn lẻ ở quy mô rất nhỏ cũng có cấu trúc hệ phân tán kiểu này (tách cache lệnh/cache dữ liệu)
    Trong Cap'n Web, chính RPC graph đóng vai trò instruction
    Mẫu này thật sự rất hấp dẫn, nhưng cũng tạo cảm giác như cấu trúc stack (compiler trên interpreter, interpreter trên compiler...) lặp vô hạn. Nó giống một phiên bản khác của kiểu tư duy Lispy: code là data, data là code. Có vẻ như đằng sau còn một câu chuyện sâu sắc hơn nữa

    • Hoàn toàn đồng ý — góc nhìn xem đây là một dạng trừu tượng phổ quát thật tuyệt
      Giờ đây ngôn ngữ động trở thành frontend cho DSL mới, nhưng thay vì định nghĩa cú pháp mới, ta nhúng việc tạo AST ngay vào script
      Tôi nghĩ TypeScript là yếu tố thay đổi cuộc chơi ở đây. Nó cho phép kết hợp sự linh hoạt ở runtime của JavaScript (như cách Cap'n Web dùng Proxy rất khéo) với độ an toàn kiểu
      Dạo này tôi rất hứng thú với khái niệm này trong mảng ORM. Hầu hết ORM đều tuần tự và eager, nên chỉ có thể can thiệp ngay trước khi thực thi truy vấn
      Một ORM thật sự composable theo tôi phải hoạt động như compiler: định nghĩa một DSL hoàn toàn type-safe trên SQL bằng TypeScript để tạo ra AST truy vấn, rồi chỉ đến cuối cùng mới compile thành SQL
      Typegres mà tôi đang phát triển cũng chính là ý tưởng đó. Nếu bạn thấy mẫu này thú vị thì có thể tham khảo
  • Vấn đề cốt lõi của thư viện RPC là chúng cố che giấu việc round-trip xảy ra ở đâu và như thế nào
    Chỉ nhìn .map() của mảng trong Cap'n Web thôi cũng khó biết thực sự network round-trip xảy ra ở đâu.
    Tôi nghĩ đây không phải là một “tính năng” mà ngược lại là một “lỗi” — nhìn vào code thì đáng lẽ phải hiểu ngay hành vi, việc che khuất điều đó là không tốt
    liên kết tham khảo

    • round-trip xảy ra khi dùng await
      promise pipelining cho phép bạn thiết lập nhiều statement liên tiếp mà không cần await, nên sẽ không có thêm vòng gọi mạng nào ở giữa. Chỉ cần await một lần ở cuối là xong
  • Nếu từng dùng gRPC với web thì bạn sẽ biết việc áp Protobuf lên web đau đớn đến mức nào
    Tôi thực sự thích sự đơn giản của Cap'n Web tài liệu capnproto
    Khác với Cap'n Proto, Cap'n Web hoàn toàn không có schema. Gần như không có boilerplate thừa, nên nó mang cảm giác rất giống RPC JavaScript native của Cloudflare Workers
    tham khảo github

  • Tôi vừa thấy thư viện mới của kentonv nên chạy vào ngay
    Nhìn mã trên GitHub, tôi ngạc nhiên vì quy mô của nó nhỏ đến không ngờ. Không biết có đúng là chỉ chừng đó thôi không
    Về lý thuyết, có vẻ việc port phần server-side sang ngôn ngữ khác cũng không quá khó; tôi muốn thử dùng nó với server Elixir và frontend JS/TS
    Cũng thú vị nếu để LLM làm việc port ngôn ngữ kiểu này. Không biết repo này có dùng mã do LLM tạo hay không. Tôi nhớ mấy tháng trước từng thấy kentonv nói về một POC do AI tạo ra (đã có người review)

    • Một phần test có dùng nội dung do LLM tạo, nhưng bản thân thư viện thì hoàn toàn không
      Ở thời điểm hiện tại, có lẽ LLM vẫn khó mà tạo ra được thư viện này. Cấu trúc bên trong của nó được thiết kế như một câu đố với nhiều mảnh ghép khớp với nhau rất tinh vi
      Thời gian dành cho việc suy nghĩ thiết kế còn nhiều hơn cả thời gian viết code thực tế
      Nó hoàn toàn khác với thư viện workers-oauth-provider, vốn là một cách triển khai mới mẻ của một spec đã quen thuộc
      Cấu trúc mã có thể dễ port sang các ngôn ngữ động như Python, nhưng tôi nghĩ sẽ khó hơn nhiều với ngôn ngữ kiểu tĩnh. Nó phụ thuộc mạnh vào các kiểu đối tượng tùy ý
  • Có những điểm giống OCapN và cũng có khác biệt quan trọng tham khảo
    Cả hai đều hỗ trợ capability transfer, promise pipelining và mô hình không schema
    Cap'n Web không có capability ngoài băng như sturdyref của OCapN (URI có thể khôi phục). Vì vậy tôi đoán nó cần xác thực bằng API key. sturdyref là một loại token không thể đoán được; chỉ cần sở hữu nó là có quyền truy cập endpoint tương ứng
    Ngoài ra, Cap'n Web cũng không có chức năng handoff ba bên, nơi Alice giới thiệu Bob cho Carol. Đây là thứ thiết yếu với ứng dụng phân tán, nên Cap'n Web có vẻ gần hơn với dịch vụ kiểu client-server SaaS truyền thống nhưng có thêm vài đặc tính của ocap

    • Tôi muốn thêm hỗ trợ 3PH sau này, nhưng ở bản phát hành đầu tiên này, ưu tiên hơn là tập trung vào giao tiếp giữa trình duyệt và web server
      Với SturdyRef, tôi nghĩ cách khôi phục sẽ khác nhau theo từng nền tảng, nên hợp lý hơn là triển khai theo từng nền tảng thay vì đưa vào cấp độ giao thức RPC
      Ví dụ, trên Cloudflare Workers, sắp tới sẽ có khả năng lưu bền capability trong Durable Object storage, nhưng cách triển khai lại rất đặc thù cho nền tảng Workers
      Sandstorm cũng có persistent capability, nhưng chỉ trong phạm vi dịch vụ nội bộ
      Vì vậy mà Cap’n Proto đã bỏ hẳn khái niệm persistent capability, và trong chuẩn web thì khái niệm gần nhất có lẽ là OAuth
      Ta có thể tưởng tượng ra một sturdyref dựa trên OAuth refresh token, nhưng đó không phải thứ có thể dùng chung trên mọi nền tảng
  • Theo như tôi xem nhanh, hệ thống này dường như yêu cầu (hoặc khuyến khích) lưu state của import/export table hay trạng thái đối tượng ở phía server theo kiểu stateful
    Trong RPC truyền thống, mọi lời gọi đều đi vào từ tầng trên cùng và mỗi lời gọi đều mang theo key v.v., nên dù yêu cầu được phân tán qua nhiều server cũng không sao; còn Cap’n Web thì không như vậy
    Tôi muốn biết liệu có thể serialize bảng đó để lưu vào DB rồi vẫn scale-out server theo cùng cách hay không, hay là bắt buộc phải có server affinity hoặc cấu trúc như Durable Objects

    • Trạng thái chỉ được duy trì trong đúng một phiên RPC
      Nếu dùng WebSocket, trạng thái tồn tại chừng nào kết nối WebSocket còn sống
      Nếu dùng truyền HTTP batch, phiên chỉ giới hạn trong toàn bộ một request HTTP, và mọi lời gọi sẽ được xử lý cùng một lần trong request đó
      Vì vậy Cap’n Web không cần duy trì trạng thái qua nhiều request/kết nối HTTP khác nhau
      Tuy vậy, nếu thiết kế của bạn khiến việc phiên bị ngắt đồng nghĩa mất toàn bộ capability, thì đó là thiết kế nên tránh. Hãy đảm bảo luôn có thể khôi phục capability sau khi reset kết nối

    • Đọc tài liệu thì có vẻ cấu trúc này dùng websocket để giữ affinity
      HTTP batching là cách gửi toàn bộ yêu cầu trong một lần rồi chờ phản hồi
      Kiểu này sẽ khiến load balancing trở nên khó hơn. Nếu có nhiều chat client, kết nối có thể dồn vào một số server nhất định, làm tăng nguy cơ quá tải ở các server đó
      Việc scale in/out server cũng trở nên phiền phức. Khi vừa phải giữ kết nối dài hạn vừa phải xử lý nhiều request đồng thời, việc quản lý sẽ rất khó
      Thêm một điều nữa, nếu client cứ liên tục gửi push event mà không bao giờ nhận response, server sẽ phải giữ các response đó trong bộ nhớ, nên tôi nghĩ rất dễ trở thành mục tiêu tấn công DDoS

    • Theo những gì tôi từng đọc trong tài liệu Cap'n Proto, server và client có thể trao đổi peer stub cho nhau
      Nếu server C nhận được từ client B một stub được tạo ở A, thì C cũng có thể gọi trực tiếp A

  • RPC is often accused of committing many of the fallacies of distributed computing.

But this reputation is outdated. When RPC was first invented some 40 years ago, async programming barely existed. We did not have Promises, much less async and await. Đoạn này làm tôi bối rối. Nếu tiền đề cốt lõi của RPC lại phụ thuộc mạnh vào một ngôn ngữ cụ thể hay mô hình đồng thời cụ thể, thì làm sao nó có thể là một giao thức được?

  • “RPC” ban đầu là một mô hình lập trình cố làm cho lời gọi từ xa trông như không thể phân biệt với lời gọi hàm nội bộ
    Để làm được vậy, trên thực tế cần có wire protocol, thư viện client/server, v.v.
    Gần đây cách hiểu đã thay đổi nhiều, và mô hình phổ biến hơn là cấu trúc giống endpoint REST nhưng có function signature
    Với sự xuất hiện của các tính năng ngôn ngữ như Future, Optional v.v., giờ đây có thể phân biệt rõ những đặc tính như “hành động này có thể bị trì hoãn” hoặc “có thể thất bại”
    Trong RPC đời đầu, tất cả những thuộc tính đó đều bị che giấu

  • Tôi không hiểu chính xác ý đó là gì. Lập trình bất đồng bộ có mặt trong rất nhiều ngôn ngữ. Tôi đã dùng gần như tất cả: JavaScript, C++, Python, Rust, C# v.v.
    Ý chính là các hệ thống RPC thời kỳ đầu chặn luồng gọi trong lúc request mạng đang diễn ra, mà đó là thiết kế rất tệ, nên giờ việc bất đồng bộ đã trở thành điều hiển nhiên

  • Tôi rất hào hứng vì Cap'n Web không bị trói vào sản phẩm Cloudflare mà tồn tại độc lập
    Khi đọc phần này trong tài liệu, tôi có một thắc mắc

    as of this writing, the feature set is not exactly the same between the two. We aim to fix this over time, by adding missing features to both sides until they match. Một khi hai bên đạt được feature parity, liệu sau đó họ có định tiếp tục giữ chúng đồng bộ không, hay cuối cùng Cap'n Web sẽ dần tụt lại sau Cloudflare Workers? Tôi cũng tò mò về khoảng cách đó

    • Kế hoạch là gần như sẽ luôn giữ hai sản phẩm đồng bộ ở những tính năng có ý nghĩa chung
      Thậm chí tôi nghĩ Cap'n Web có thể đi trước worker RPC ở một số điểm (thực tế tính năng pipeline đã đi trước rồi)
      Cấu trúc của Cap'n Web đơn giản hơn nhiều, nên các thử nghiệm tính năng mới có lẽ cũng sẽ được làm trước ở Cap'n Web