Ngôn ngữ được biên dịch sang JS - cách thanh lịch nhất để thực hiện chờ đợi theo kiểu đồng bộ


8

Tôi đang cố gắng tạo ngôn ngữ (một ngôn ngữ khác) biên dịch sang JavaScript. Một trong những tính năng tôi muốn có là khả năng thực hiện các hoạt động không đồng bộ của JavaScript một cách đồng bộ (tất nhiên là không đồng bộ - mà không chặn luồng chính, tất nhiên). Ít nói hơn, nhiều ví dụ hơn:

/* These two snippets should do exactly the same thing */

// the js way
var url = 'file.txt';
fetch(url)
    .then(function (data) {
        data = "(" + data + ")";
        return sendToServer(data);
    }).then(function (response) {
        console.log(response);
    });

// synchronous-like way
var url = 'file.txt';
var response = sendToServer("(" + fetch(url) + ")");
console.log(response);

Cách thanh lịch nhất để biên dịch nó trở lại với JS là gì?

Các tiêu chí, được sắp xếp theo mức độ quan trọng:

  1. Hiệu suất
  2. Khả năng tương thích ngược
  3. Mã biên dịch dễ đọc (không thực sự quan trọng)

Có một số cách có thể để thực hiện nó, ba cách đó xuất hiện trong đầu tôi:

  1. Sử dụng lời hứa:

    • f(); a( b() ); sẽ biến thành
    • f().then(function(){ return b() }).then(function(x){ a(x) });
    • ưu điểm: tương đối đơn giản để thực hiện, polyfillable trở lại ES3
    • Nhược điểm: tạo một hàm mới và một thể hiện Proxy cho mỗi lệnh gọi hàm
  2. Sử dụng máy phát điện :

    • f(); a( b() ); sẽ biến thành
    • yield f(); tmp = yield b(); yield a( tmp );
    • ưu: javascript đẹp hơn
    • Nhược điểm: tạo proxy, trình tạo và lặp trên mỗi bước, cũng không phải là polyfillable
    • EDIT: trên thực tế chúng là polyfillable khi sử dụng một trình biên dịch lại khác (ví dụ: Regenerator )
  3. Sử dụng XMLHttpRequest đồng bộ:

    • yêu cầu tập lệnh máy chủ chờ cho đến khi có cuộc gọi khác từ nơi khác trong mã
    • ưu điểm: thực tế không có thay đổi nào trong mã, siêu dễ thực hiện
    • Nhược điểm: yêu cầu tập lệnh máy chủ (=> không có tính di động, chỉ dành cho trình duyệt); hơn nữa, XMLHttpRequest đồng bộ chặn luồng để nó không hoạt động
    • EDIT: Nó thực sự sẽ hoạt động bên trong Công nhân

Vấn đề chính là người ta không thể biết nếu một chức năng không đồng bộ cho đến khi chạy. Điều đó dẫn đến hai giải pháp ít nhất là làm việc chậm hơn rất nhiều so với JS thuần túy. Vì vậy tôi hỏi:

Có cách nào tốt hơn để thực hiện?

Từ câu trả lời:

Đang chờ từ khóa (@ MI3Guy)

  • Cách C # ( tốt !)
  • giới thiệu một từ khóa mới (có thể được ngụy trang thành một hàm (công dân hạng 1), ví dụ như ném nếu lập trình viên cố gắng vượt qua nó như một đối số)
  • Làm thế nào để biết chức năng đó không đồng bộ? Không có từ khóa khác!
    • Mọi chức năng có chứa awaittừ khóa trở nên không đồng bộ?
    • Khi mã được await, nó trả lại một lời hứa? Khác coi như một chức năng bình thường?

11
synchronously (without blocking the thread, of course) Bạn tiếp tục sử dụng từ đó. Tôi không nghĩ nó có nghĩa là những gì bạn nghĩ, bởi vì đồng bộ có nghĩa là "chặn chuỗi".
Mason Wheeler


1
@MasonWheeler: Tôi giả sử op có nghĩa là anh ta muốn sử dụng cú pháp kiểu đồng bộ.
Brian

Tôi sẽ không gọi đây là câu trả lời, nhưng những gì bạn đang cố nghe có vẻ giống như những gì C # làm với các chức năng không đồng bộ của nó. Tôi cảm thấy như phương thức phù hợp nhất với các giá trị trả về sẽ là việc sử dụng các lời hứa và vâng, điều này sẽ liên quan đến rất nhiều hàm ẩn danh. Bạn có thể thử tìm các bài viết về cách biên dịch mã C # để tham khảo; nó có thể chỉ là không hiệu quả trong một số cách.
Katana314

2
f(); a( b() );sẽ chuyển sang f().then(b).then(a);Bạn không kèm theo các chức năng.
thefourtheye

Câu trả lời:


3

Giả sử ngôn ngữ được gõ động, tôi có thể thấy hai cách khác nhau có thể được xử lý mà không biết tại thời điểm biên dịch nếu cuộc gọi không đồng bộ.

Một cách tiếp cận sẽ là giả sử mọi cuộc gọi có thể không đồng bộ. Tuy nhiên, điều này sẽ cực kỳ kém hiệu quả và tạo ra rất nhiều mã bổ sung. Đây thực chất là những gì bạn đang làm trong tùy chọn 2.

Một lựa chọn khác là sử dụng sợi. Sợi giống như các luồng nhẹ được chương trình lên lịch. Các sợi tự quyết định khi từ bỏ kiểm soát. Tuy nhiên, những yêu cầu này hỗ trợ hệ điều hành. Theo như tôi có thể nói, cách duy nhất để sử dụng các sợi trong JavaScript là trong node.js. Tôi đoán là nếu bạn đang biên dịch cho JS rằng mục tiêu (hoặc ít nhất là một mục tiêu) là chạy trên trình duyệt loại trừ tùy chọn này. Đây sẽ là phiên bản nhẹ hơn của tùy chọn 3.

Thành thật mà nói, tôi sẽ xem xét thêm một từ khóa như C # await. Điều này có một số lợi thế ngoài việc chỉ định rằng một chức năng là không đồng bộ. Các chức năng có thể được gọi để tạo ra tương lai mà không cần chờ chúng ngay lập tức. Điều này cho phép nhiều hoạt động không đồng bộ xảy ra cùng một lúc thay vì theo trình tự. Ngoài ra, tốt hơn là nên rõ ràng về những hoạt động không đồng bộ vì mã khác có thể chạy trong khi hoạt động đang diễn ra. Nếu async là tự động, có thể gây ngạc nhiên khi một số dữ liệu bạn đang sử dụng được sửa đổi bởi một đoạn mã bên ngoài.

Trong C # trong một phương thức được đánh dấu là không đồng bộ, câu lệnh return được sử dụng để trả về giá trị sẽ nằm trong tương lai, chứ không phải chính tương lai. Tuy nhiên, lưu ý rằng công cụ sửa đổi async không thực sự là một phần của chữ ký phương thức. awaitcó thể được sử dụng với bất kỳ phương pháp nào trả về tương lai.

Nếu bạn không muốn thêm một công cụ sửa đổi async cho các chức năng, bạn có thể quyết định (như bạn đã nêu) rằng bất kỳ chức năng nào đang chờ được xử lý giống như nó có một công cụ sửa đổi async ẩn. Vì vậy, ngay cả khi một hàm có một await, nhưng trả về trước khi tiếp cận nó, hàm vẫn sẽ trả về một tương lai. Tôi cũng khuyên bạn nên tiết lộ một số cách để chuyển đổi một giá trị thành một tương lai hoàn thành có chứa giá trị đó, đặc biệt là nếu không có cách nào để thực hiện điều đó một cách ngầm định bằng cách thêm công cụ sửa đổi async.


Mục tiêu ban đầu của tôi là tạo ra một ngôn ngữ hoàn toàn phù hợp, không có cấu trúc nào không thể sử dụng lại (ví dụ: nếu có if(){} else{}, lập trình viên cũng có thể định nghĩa một cấu trúc tương tự như ifZero(){} elseWhile(){}). Nhưng có vẻ như tôi phải giới thiệu một hàm chờ toàn cầu sẽ ném (hoặc ít nhất trả về không xác định) nếu lập trình viên cố gắng gán giá trị của nó cho một biến (các hàm là công dân hạng 1).
m93a

Cập nhật câu hỏi (dưới cùng). Bạn vui lòng cho tôi biết bạn nghĩ gì về những câu hỏi ở đó? Cảm ơn.
m93a

Tôi đã thêm chi tiết dựa trên cập nhật của bạn. Hãy cho tôi biết nếu bạn có ý nghĩa khác.
MI3Guy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.