web-dev-qa-db-ja.com

非同期呼び出しを含むJavaScript関数を途中で同期することは可能ですか?

データがオンザフライで要求される場合に、JavaScript関数を中間的に同期する方法が存在するかどうか疑問に思っています。私はそれを純粋にブロッキングにしたくはありませんが(これは不可能だと思います)、全体的なアルゴリズムを1つのピースに書くための素晴らしい方法を求めています。

次の例を見てみましょう。

function(){
    command1;
    var x1 = something;
    async_function(x1, callback1);
}
function callback1(data){
    var x2 = command2(data);
    async_function2(x2, callback2);
}
function callback2(data1){
    command3(data1);
}

私は次のようなものを書きたいと思います:

function(){
    command1;
    var x1 = something;
    call(async_function, [x1], callback1);
    var x2 = command2(data);
    call(async_function2, [x2], callback2);
    command3(data1);
}
  • 関数のcallerプロパティをいじってみましたが、実行環境に慣れていません。
  • apply関数を使用して、非同期関数の結果を呼び出し元に返すカスタムコールバックを渡して、上記のような関数 "call"も作成しました。

私のバグは、プログラミング\デバッグ中に、ある関数から別の関数へとコードを追跡しなければならないことです(映画の開始と同じように)。高レベルのアルゴリズムを多くの関数に分解せずに1つの場所で記述したい。
たとえば、A *アルゴリズムを記述したいが、「getNeighbors」関数が非同期の場合。

4
Ramzi Kahil

ここであなたが本当に話しているのは、スタイル/読みやすさの問題です(少なくとも、それは私が読む方法です)。

実際には、2つのオプションがあります。上記のように複数の異なる関数を作成するか、クロージャーを使用してすべてを「インライン」にします。これにより、水平空白の正気を保つことで問題がすぐに発生します。

そうは言っても、本当にすべてを1つのプロシージャのように見える単一のブロックに保持したい場合は、おそらくクロージャーオプションを使用します。

function(){
    command1;
    var x1 = something;
    async_function(x1, function (data){
        var x2 = command2(data);
        async_function2(x2, function (data1){
            command3(data1);
        });
    });
}

もちろん、これは次のように間隔を空けることができ、希望の例に非常に近くなります。

function(){
    command1;
    var x1 = something;
    async_function(x1, function (data){
    var x2 = command2(data);
    async_function2(x2, function (data1){
    command3(data1);
    });});
}

...しかし個人的には、おそらく最初のバージョンに固執するでしょう。

一般的に私もしませんが。私はそれを小さくて明らかに名前が付けられたルーチンに分解し続けます、それは長い目で見ればはるかに読みやすいコードになり、どんな正気なIDEは、関数呼び出しから関数定義にジャンプすることを可能にします逆も同様なので、ナビゲーションはそれほど問題になりません。

5
DaveRandom

これがあなたが遭遇している問題を解決することを保証することはできませんが、Queuingは、一連の非同期呼び出しを連鎖させて、次々と起こります。

少し前に Queue スクリプトを記述しましたが、これを例として使用します。

(function () {
    var q = new Queue();
    q.queue(function (next) {
        //this gets run first
        //next is used to dequeue the next item in the queue
        someAsyncCall(someData, /*callback*/next);
    }, function (next) {
        //this gets run second
        someOtherAsyncCall(moreData, next);
    }, function (next) {
        //this gets run third
        moarAsync(allthedata, next);
    });
}());

キューを使用すると、大量のネストを行わなくても、コードフローを直線的に記述できます。この特定の実装は、直接渡されるデータを処理しません。

4
zzzzBov

Deferreds を調べてください。

関数内のコードが非同期であるという事実に関係なく、関数を実装して順次実行することができます。最初が終了すると、2番目がトリガーされます。

4
rusev

ワークフローにコンパイル手順を追加してもかまわない場合は、次のリストにある同期から非同期へのコンパイラのいずれかを使用することを検討できます。

https://github.com/jashkenas/coffee-script/wiki/List-of-languages-that-c​​ompile-to-JS

(「Synchronous to Asynchronous JavaScript Compilers」セクションに移動します)

ほとんどの場合、ライブラリベースのソリューションを使用しても問題ありませんが、非同期対応の方言を使用すると、コードが可能な限り「同期のような」ものになります。 (これは、returnステートメントやbreakステートメントなど、ループやジャンプがたくさんある場合に最も顕著です。これらの場合、継続を使用して物事を書くのは面倒です)

1
hugomg

私が正しく理解していれば、非同期呼び出しのパイプ処理について話していることになります。

@rusevが述べたように、これはいわゆる Deffered objects で実行できます。以下は、ブロッキングと非同期のものを組み合わせる例です。

var dfd = $.Deferred();

dfd.pipe(function (data) {
    // Normal blocking computations...
    var result = data.a + data.b;
    var output = $('<p>').text('Add: ' + result);
    $('body').append(output);

    return result;
}).pipe(function (data) {
    // Async AJAX call...
    return $.post('/echo/json/', { json: '{ "value": ' + data + ' }' });
}).pipe(function (data) {
    // Again some normal computations...
    var result = data.value * data.value;
    var output = $('<p>').text('Square: ' + result);
    $('body').append(output);

    return result;
}).pipe(function (data) {
    // AJAX...
    return $.post('/echo/json/', { json: '{ "value": ' + data + ' }' });
}).pipe(function (data) {
    // Normal...
    var result = data.value + 1;
    var output = $('<p>').text('Increment: ' + result);

    $('body').append(output);
});

dfd.resolve({a: 1, b: 2}); // Initial values

http://jsfiddle.net/sRFRC/

ご覧のとおり、データは1つのコールバックから別のコールバックに順番に渡されますが、ネストされた方法で書き込む必要はありません。

0
scriptin

Promises/A + https://promisesaplus.com/ をご覧ください。来るecma6ネイティブサポートの代わりにPromises/A +の素晴らしい実装は https://github.com/tildeio/rsvp.js/ です

0
Jan Míšek