290

私は2つのJS関数を持っています。一方が他方を呼び出します。呼び出し元の関数内で、もう一方を呼び出し、その関数が終了するのを待ってから続行したいと思います。したがって、たとえば/疑似コード:

function firstFunction(){
    for(i=0;i<x;i++){
        // do something
    }
};

function secondFunction(){
    firstFunction()
    // now wait for firstFunction to finish...
    // do something else
};

私はこの解決策を思いつきましたが、これが賢明な方法であるかどうかはわかりません。

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()
    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

それは合法ですか?それを処理するよりエレガントな方法はありますか?おそらくjQueryで?

4

10 に答える 10

200

このような非同期作業を処理する 1 つの方法は、コールバック関数を使用することです。

function firstFunction(_callback){
    // do some asynchronous work
    // and when the asynchronous stuff is complete
    _callback();    
}

function secondFunction(){
    // call first function and pass in a callback function which
    // first function runs when it has completed
    firstFunction(function() {
        console.log('huzzah, I\'m done!');
    });    
}

@Janaka Pushpakumara の提案に従って、矢印関数を使用して同じことを達成できるようになりました。例えば:

firstFunction(() => console.log('huzzah, I\'m done!'))


更新:かなり前にこれに答えましたが、本当に更新したいと思います。コールバックはまったく問題ありませんが、私の経験では、コードの読み取りと保守がより困難になる傾向があります。ただし、進行中のイベントなどをパラメーターとして渡すなど、まだ使用している状況があります。この更新は、代替案を強調するためのものです。

また、元の質問では非同期について具体的に言及していないため、誰かが混乱した場合に備えて、関数が同期的である場合、呼び出されたときブロックされます。例えば:

doSomething()
// the function below will wait until doSomething completes if it is synchronous
doSomethingElse()

関数が非同期であることが暗示されている場合でも、今日のすべての非同期作業を処理する方法は、async/await を使用することです。例えば:

const secondFunction = async () => {
  const result = await firstFunction()
  // do something else here after firstFunction completes
}

IMO、async/await を使用すると、promise を直接使用するよりもコードがはるかに読みやすくなります (ほとんどの場合)。キャッチエラーを処理する必要がある場合は、try/catch で使用してください。詳細については、https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function を参照してください。

于 2014-02-03T01:24:05.880 に答える
58

ここで重要な点を見落としているようです: JavaScript はシングルスレッドの実行環境です。コードをもう一度見てみましょう。追加したことに注意してくださいalert("Here")

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()

    alert("Here");

    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

を待つ必要はありませんisPaused。「Here」アラートが表示されたら、isPausedすでにfalse戻っfirstFunctionていることになります。forこれは、ループ ( )内から「譲歩」できないためです。// do somethingループは中断されない可能性があり、最初に完全に完了する必要があります (詳細: Javascript スレッド処理と競合条件)。

とはいえ、内部のコード フローをfirstFunction非同期にして、callback または promise のいずれかを使用して呼び出し元に通知することもできます。forループをあきらめて、if代わりにシミュレートする必要があります ( JSFiddle ):

function firstFunction()
{
    var deferred = $.Deferred();

    var i = 0;
    var nextStep = function() {
        if (i<10) {
            // Do something
            printOutput("Step: " + i);
            i++;
            setTimeout(nextStep, 500); 
        }
        else {
            deferred.resolve(i);
        }
    }
    nextStep();
    return deferred.promise();
}

function secondFunction()
{
    var promise = firstFunction();
    promise.then(function(result) { 
        printOutput("Result: " + result);
    });
}

余談ですが、JavaScript 1.7 では、ジェネレーターyieldの一部としてキーワードが導入されました。これにより、同期 JavaScript コード フローの非同期ホールを「パンチ」することができます (詳細と例)。ただし、現在、ジェネレーターのブラウザー サポートは Firefox と Chrome に限定されています。

于 2014-02-03T09:59:55.273 に答える