Saya mencoba membuat (lagi) bahasa yang mengkompilasi ke JavaScript. Salah satu fitur yang saya ingin miliki adalah kemampuan untuk melakukan operasi async JavaScript secara sinkron (tidak persis sinkron - tanpa memblokir utas utama, tentu saja). Lebih sedikit bicara, lebih banyak contoh:
/* 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);
Apa cara paling elegan untuk mengkompilasinya kembali ke JS?
Kriteria, diurutkan berdasarkan kepentingan:
- Performa
- Kompatibilitas mundur
- Kode yang dapat dibaca (tidak terlalu penting)
Ada beberapa cara yang mungkin untuk mengimplementasikannya, ketiganya muncul di pikiran saya:
Menggunakan Janji:
f(); a( b() );
akan berubah menjadif().then(function(){ return b() }).then(function(x){ a(x) });
- pro: relatif mudah diimplementasikan, polyfillable kembali ke ES3
- kontra: membuat fungsi baru dan instance Proxy untuk setiap panggilan fungsi
-
f(); a( b() );
akan berubah menjadiyield f(); tmp = yield b(); yield a( tmp );
- pro: javascript lebih bagus
- kontra: membuat proxy, generator dan iterator pada setiap langkah, juga tidak bisa diisi polyfillable
- EDIT: sebenarnya mereka bisa diisi dengan polyfillable menggunakan recompiler lain (mis. Regenerator )
Menggunakan XMLHttpRequest sinkron:
- meminta skrip server yang menunggu hingga panggilan lain dari tempat lain dalam kode
- pro: de facto tidak ada perubahan dalam kode, super mudah diimplementasikan
- kontra: memerlukan skrip server (=> tanpa portabilitas, khusus browser); selanjutnya, XMLHttpRequest sinkron memblokir utas sehingga tidak berfungsi sama sekali
- EDIT: Ini sebenarnya akan bekerja di dalam Pekerja
Masalah utama adalah bahwa seseorang tidak dapat mengetahui apakah suatu fungsi tidak sinkron sampai runtime. Itu menghasilkan dua solusi yang setidaknya bekerja jauh lebih lambat daripada JS murni. Jadi saya bertanya:
Apakah ada cara yang lebih baik untuk diterapkan?
Dari jawaban:
Menunggu kata kunci (@ MI3Guy)
- C # way (mana yang bagus !)
- memperkenalkan kata kunci baru (yang dapat disamarkan sebagai fungsi (warga negara kelas 1) yang mis. melempar jika programmer mencoba untuk meneruskannya sebagai argumen)
- Bagaimana cara mengetahui fungsi itu asinkron? Tidak ada kata kunci lain!
- Setiap fungsi yang mengandung
await
kata kunci menjadi tidak sinkron? - Ketika kode sampai
await
, itu mengembalikan janji? Lain memperlakukan sebagai fungsi biasa?
- Setiap fungsi yang mengandung
synchronously (without blocking the thread, of course)
Anda tetap menggunakan kata itu. Saya tidak berpikir itu berarti apa yang Anda pikirkan artinya, karena secara serempak berarti "memblokir utas".f(); a( b() );
akan berubah menjadif().then(b).then(a);
Anda tidak menyertakan fungsi.Jawaban:
Dengan asumsi bahasa diketik secara dinamis, saya dapat melihat dua cara berbeda ini dapat ditangani tanpa mengetahui pada waktu kompilasi jika panggilan adalah async.
Salah satu pendekatan akan mengasumsikan setiap panggilan bisa async. Namun, ini akan sangat tidak efisien dan menghasilkan banyak kode tambahan. Ini pada dasarnya adalah apa yang Anda lakukan pada opsi 2.
Pilihan lain adalah menggunakan serat. Serat seperti benang ringan yang dijadwalkan oleh program. Serat itu sendiri memutuskan kapan harus menyerahkan kontrol. Namun, ini memerlukan dukungan sistem operasi. Sejauh yang saya tahu, satu-satunya cara untuk menggunakan serat dalam JavaScript adalah dalam node.js. Dugaan saya adalah bahwa jika Anda mengkompilasi ke JS bahwa tujuan (atau setidaknya tujuan) dijalankan pada browser yang mengesampingkan opsi ini. Ini akan menjadi versi opsi 3 yang lebih ringan.
Jujur, saya akan mempertimbangkan untuk menambahkan kata kunci seperti C # 's
await
. Ini memiliki sejumlah keunggulan selain menspesifikasikan bahwa suatu fungsi adalah async. Fungsi dapat dipanggil untuk menghasilkan masa depan tanpa menunggu mereka segera. Ini memungkinkan beberapa operasi async terjadi sekaligus daripada berurutan. Selain itu, lebih baik untuk eksplisit tentang operasi apa yang async karena kode lain dapat berjalan saat operasi berlangsung. Jika async otomatis, mungkin mengejutkan ketika beberapa data yang Anda gunakan dimodifikasi oleh potongan kode eksternal.Dalam C # dalam metode yang ditandai sebagai async, pernyataan kembali digunakan untuk mengembalikan nilai yang akan berada di dalam masa depan, bukan masa depan itu sendiri. Perhatikan, bagaimanapun, bahwa pengubah async tidak benar-benar bagian dari tanda tangan metode.
await
dapat digunakan dengan metode apa pun yang mengembalikan masa depan.Jika Anda tidak ingin menambahkan pengubah async untuk fungsi, Anda dapat memutuskan (seperti yang Anda nyatakan) bahwa setiap fungsi dengan menunggu diperlakukan seperti memiliki pengubah async implisit. Jadi, bahkan jika suatu fungsi memiliki
await
, tetapi kembali sebelum mencapainya, fungsi tersebut masih harus mengembalikan masa depan. Saya juga akan merekomendasikan memaparkan beberapa cara untuk mengubah nilai menjadi masa depan yang lengkap yang berisi nilai, terutama jika tidak ada cara untuk melakukannya secara implisit dengan menambahkan pengubah async.sumber
if(){} else{}
, pemrogram juga dapat mendefinisikan struktur serupa sepertiifZero(){} elseWhile(){}
). Tapi sepertinya saya harus memperkenalkan fungsi menunggu global yang melempar (atau setidaknya mengembalikan tidak terdefinisi) jika programmer mencoba untuk menetapkan nilainya ke variabel (fungsi adalah warga negara kelas 1).