JSにコンパイルされた言語–同期式の待機を行う最もエレガントな方法


8

JavaScriptにコンパイルする(まだ別の)言語を作成しようとしています。私が持たせたい機能の1つは、JavaScriptの非同期操作を同期的に(正確には同期ではなく、もちろんメインスレッドをブロックせずに)実行できることです。より少ない話、より多くの例:

/* 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);

それをJSにコンパイルする最もエレガントな方法は何ですか?

重要度でソートされた基準:

  1. パフォーマンス
  2. 下位互換性
  3. コンパイルされたコードの可読性(それほど重要ではない)

それを実装するいくつかの可能な方法があり、それらの3つは私の頭に浮かびました:

  1. Promiseの使用:

    • f(); a( b() ); に変わります
    • f().then(function(){ return b() }).then(function(x){ a(x) });
    • 長所:実装が比較的簡単で、ES3にポリフィル可能
    • 短所:関数呼び出しごとに新しい関数とProxyインスタンスを作成する
  2. ジェネレーターの使用

    • f(); a( b() ); に変わります
    • yield f(); tmp = yield b(); yield a( tmp );
    • 長所:より良いJavaScript
    • 短所:すべてのステップでプロキシ、ジェネレータ、イテレータを作成し、ポリフィルも不可
    • 編集:実際には、別のリコンパイラー(例:Regenerator)を使用してポリフィルできます。
  3. 同期XMLHttpRequestの使用:

    • コード内の別の場所からの別の呼び出しまで待機するサーバースクリプトを要求する
    • 長所:コードの変更は事実上なく、実装が非常に簡単
    • 短所:サーバースクリプトが必要(=>移植性なし、ブラウザーのみ); さらに、同期XMLHttpRequestはスレッドをブロックするため、まったく機能しません
    • 編集:それは実際にはワーカー内で機能します

主な問題は、実行時まで関数が非同期かどうかわからないことです。その結果、少なくとも動作する2つのソリューションは、純粋なJSよりもはるかに遅くなります。だから私は尋ねます:

実装するより良い方法はありますか?

回答から:

Awaitキーワード(@ MI3Guy)

  • C#の方法(これは良いことです!)
  • 新しいキーワードを導入します(これは関数(1st-class-citizen)として偽装することができます。たとえば、プログラマーが引数としてそれを渡そうとした場合にスローされます)
  • その関数が非同期であることをどのように知るのですか?他のキーワードはありません!
    • awaitキーワードを含むすべての関数が非同期になりますか?
    • コードがに到達するawaitと、promiseを返しますか?そうでなければ、通常の関数として扱いますか?

11
synchronously (without blocking the thread, of course) あなたはその言葉を使い続けます。同期的には「スレッドをブロックする」
メイソンウィーラー、


1
@MasonWheeler:opは同期スタイルの構文を使用したいという意味だと思います。
ブライアン

私はこれを答えとは言いませんが、あなたがしようとしていることは、C#が非同期関数で行うことと少し似ています。戻り値を使用する最も一貫性のあるメソッドはプロミスを使用することだと思います。そうです、これには多くの匿名関数が含まれます。C#コードが参照用にトランスコンパイルされる方法に関する記事を検索してみてください。それはおそらくいくつかの点で同様に非効率的です。
Katana314

2
f(); a( b() );向けるだろうf().then(b).then(a);な機能を囲んでいないあなた。
thefourtheye 2015

回答:


3

言語が動的に型付けされていると仮定すると、呼び出しが非同期かどうかをコンパイル時に知らなくても、これを処理できる2つの異なる方法がわかります。

1つのアプローチは、すべての呼び出しが非同期であると想定することです。ただし、これは信じられないほど非効率的であり、多くの余分なコードを生成します。これは基本的に、オプション2で行っていることです。

別のオプションは繊維を使用することです。ファイバーは、プログラムによってスケジュールされる軽量スレッドのようなものです。ファイバー自体が制御を放棄するタイミングを決定します。ただし、これらにはオペレーティングシステムのサポートが必要です。私が知る限り、JavaScriptでファイバーを使用する唯一の方法は、node.js内です。私の推測では、JSにコンパイルしている場合、目標(または少なくとも目標)は、このオプションを除外するブラウザーで実行することです。これは、オプション3のより軽量なバージョンになります。

正直なところ、C#のようなキーワードを追加することを検討しawaitます。これには、関数が非同期であることを指定することに加えて、いくつかの利点があります。関数は、先物をすぐに待たずに生成するために呼び出すことができます。これにより、複数の非同期操作を順番にではなく一度に実行できます。さらに、操作の実行中に他のコードが実行される可能性があるため、非同期の操作について明示することをお勧めします。asyncが自動の場合、使用しているデータの一部が外部のコードによって変更されたときに驚くかもしれません。

非同期としてマークされたメソッド内のC#では、returnステートメントを使用して、フューチャー自体ではなくフューチャー内にある値を返します。ただし、async修飾子は実際にはメソッドシグネチャの一部ではないことに注意してください。awaitfutureを返す任意のメソッドで使用できます。

関数に非同期修飾子を追加したくない場合は、(述べたように)awaitのある関数は、暗黙の非同期修飾子を持つように扱われるように決定できます。したがって、関数にがあっても、await到達する前に戻る場合でも、関数はフューチャーを返す必要があります。特にasync修飾子を追加して暗黙的に行う方法がない場合は、値を含む完全なフューチャーに値を変換する方法を公開することもお勧めします。


私の当初の目標は、再利用できない構造のない完全に一貫した言語を作成することでした(たとえば、がある場合if(){} else{}、プログラマーはのような同様の構造を定義することもできますifZero(){} elseWhile(){})。しかし、プログラマーが変数に値を割り当てようとすると(または関数がファーストクラスシチズン)、スローする(または少なくとも未定義を返す)グローバル待機関数を導入する必要があるようです。
m93a

質問を更新しました(一番下)。そこで質問についてどう思いますか?ありがとう。
m93a

更新に基づいて詳細を追加しました。別の意味があるかどうかをお知らせください。
MI3Guy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.