web-dev-qa-db-ja.com

javascriptコードを*順番に*実行するにはどうすればよいですか?

さて、JavascriptがC#でもPHPでもないことを感謝しますが、私はJavascriptの問題に戻ってきます-JS自体ではなく、それを使用しています。

私は機能を持っています:

function updateStatuses(){

showLoader() //show the 'loader.gif' in the UI

updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');

hideLoader(); //hide the 'loader.gif' in the UI

}

つまり、Javascriptがコードを先に進めたいという熱烈な願望のために、「hideLoader」関数が直後に実行されるため、ローダーが表示されることはありません。

どうすればこれを修正できますか?言い換えれば、JavaScript関数をページに書き込んだ順序で実行するにはどうすればよいですか...

20
Ed.

この問題は、AJAXは本質的に非同期であるために発生します。これは、updateStatus()呼び出しが実際に順番に実行されるが、すぐに戻り、JSインタープリターがhideLoader()に到達することを意味します。 AJAXリクエストからデータが取得される前。

AJAX呼び出しが終了したイベントでは、hideLoader()を実行する必要があります。

14
anorm

AJAXプログラミングを行う場合は、JavaScriptを手続き型ではなく、イベントベースと考える必要があります。最初の呼び出しが完了するまで待ってから2番目の呼び出しを実行する必要があります。その方法は次のとおりです。 2番目の呼び出しを最初の呼び出しが終了したときに発生するコールバックにバインドします。AJAXライブラリ(ライブラリを使用していることを願っています)の内部動作について詳しく知らなければ、わかりません。これを行う方法ですが、おそらく次のようになります。

_showLoader();

  updateStatus('cron1', function() {
    updateStatus('cron2', function() {
      updateStatus('cron3', function() {
        updateStatus('cronEmail', function() {
          updateStatus('cronHourly', function() {
            updateStatus('cronDaily', funciton() { hideLoader(); })
          })
        })
      })
    })
  })
});
_

アイデアは、updateStatusが通常の引数に加えて、終了時に実行するコールバック関数を受け取るというものです。 onCompleteを実行する関数を、そのようなフックを提供する関数に渡すことは、かなり一般的なパターンです。

更新

JQueryを使用している場合は、ここで$.ajax()を読むことができます: http://api.jquery.com/jQuery.ajax/

コードはおそらく次のようになります。

_function updateStatus(arg) {
  // processing

  $.ajax({
     data : /* something */,
     url  : /* something */
  });

  // processing
}
_

次のようなものを使用して、2番目のパラメーターとしてコールバックを受け取るように関数を変更できます。

_function updateStatus(arg, onComplete) {
  $.ajax({
    data : /* something */,
    url  : /* something */,
    complete : onComplete // called when AJAX transaction finishes
  });
_

}

13
meagar

私はあなたがする必要があるのはあなたのコードにこれを持っていることだけだと思います:

async: false,

したがって、Ajax呼び出しは次のようになります。

jQuery.ajax({
            type: "GET",
            url: "something.html for example",
            dataType: "html",
            async: false,
            context: document.body,
            success: function(response){

                //do stuff here

            },
            error: function() {
                alert("Sorry, The requested property could not be found.");
            }  
        });

明らかに、これの一部はXMLJSONなどで変更する必要がありますが、async: false,は、成功の呼び出しが戻る(または失敗する)まで待機してから続行するようにJSエンジンに指示するここでの要点です。これには欠点があることを忘れないでください。それは、ajaxが戻るまでページ全体が応答しなくなることです!!!通常はミリ秒以内で、大したことではありませんが、もっと時間がかかる場合があります。

これが正しい答えであり、それがあなたを助けることを願っています:)

10
Amir

私たちのプロジェクトの1つに似たようなものがあり、カウンターを使用してそれを解決しました。 updateStatusの呼び出しごとにカウンターを増やし、AJAXリクエストの応答関数で減らすと(AJAX JavaScriptライブラリに依存します)使用しています。)

カウンターがゼロに達すると、すべてのAJAXリクエストが完了し、hideLoader()を呼び出すことができます。

サンプルは次のとおりです。

var loadCounter = 0;

function updateStatuses(){
    updateStatus('cron1'); //performs an ajax request to get the status of something
    updateStatus('cron2');
    updateStatus('cron3');    
    updateStatus('cronEmail');
    updateStatus('cronHourly');
    updateStatus('cronDaily');
}

function updateStatus(what) {
    loadCounter++;

    //perform your AJAX call and set the response method to updateStatusCompleted()
}

function updateStatusCompleted() {
    loadCounter--;
    if (loadCounter <= 0)
        hideLoader(); //hide the 'loader.gif' in the UI
}
5
Prutswonder

これは、コードの実行順序とは関係ありません。

ローダーイメージが表示されない理由は、関数の実行中にUIが更新されないためです。 UIで変更を行った場合、関数を終了してブラウザに制御を戻すまで、変更は表示されません。

画像を設定した後にタイムアウトを使用して、残りのコードを開始する前にブラウザにUIを更新する機会を与えることができます。

function updateStatuses(){

  showLoader() //show the 'loader.gif' in the UI

  // start a timeout that will start the rest of the code after the UI updates
  window.setTimeout(function(){
    updateStatus('cron1'); //performs an ajax request to get the status of something
    updateStatus('cron2');
    updateStatus('cron3');
    updateStatus('cronEmail');
    updateStatus('cronHourly');
    updateStatus('cronDaily');

    hideLoader(); //hide the 'loader.gif' in the UI
  },0);
}

コードが順不同で実行されているように見える可能性がある別の要因があります。 AJAXリクエストが非同期の場合、関数は応答を待機しません。ブラウザが応答を受信すると、応答を処理する関数が実行されます。ローダーを非表示にする場合応答を受信した後の画像では、最後の応答ハンドラー関数の実行時にこれを行う必要があります。応答は要求を送信した順序で到着する必要がないため、取得した応答の数を数える必要があります。最後のものがいつ来るかを知るために。

2
Guffa

他の人が指摘しているように、あなたは同期操作をしたくありません。非同期を受け入れる、それがAJAXのAの略です。

同期と非同期の優れたアナロジーについて触れておきたいと思います。 GWTフォーラムの投稿全体 を読むことができます。関連するアナロジーを含めています。

あなたがするかどうか想像してみてください...

あなたはテレビを見ながらソファに座っていて、ビールがなくなったことを知っているので、配偶者に酒屋に駆け寄ってあなたにビールを持ってきてくれるように頼みます。配偶者が玄関から出て行くのを見るとすぐに、あなたはソファから起き上がり、台所に身を寄せて冷蔵庫を開けます。驚いたことに、ビールはありません!

もちろんビールはありません、あなたの配偶者はまだ酒屋に旅行中です。ビールを飲むことを期待できるようになる前に、彼が戻るまで待たなければなりません。

しかし、あなたはそれを同期させたいと言いますか?もう一度想像してみてください...

...配偶者がドアから出て行く...今、あなたの周りの世界全体が止まり、あなたは息を吸ったり、ドアに答えたり、ショーを見終えたりすることができません。筋肉を動かさずにそこに座り、意識を失うまで青くなります...しばらくして、EMTと配偶者に囲まれて目を覚ますと、ああ、ねえ、私はあなたのビールを手に入れました。

同期サーバー呼び出しを行うことを主張すると、まさにそれが起こります。

1

Firebugをインストールしてから、showLoader、updateStatus、hideLoaderのそれぞれに次のような行を追加します。

Console.log("event logged");

コンソールウィンドウに関数の呼び出しが一覧表示され、順番に表示されます。問題は、「updateStatus」メソッドは何をするのかということです。

おそらく、バックグラウンドタスクを開始してから戻るため、バックグラウンドタスクが終了する前にhideLoaderの呼び出しに到達します。 Ajaxライブラリにはおそらく「OnComplete」または「OnFinished」コールバックがあります-そこから次のupdateStatusを呼び出します。

1
Douglas

すべての非同期リクエストを処理するための最良のソリューションの1つは、 'Promise'です。
Promiseオブジェクトは、非同期操作の最終的な完了(または失敗)を表します。

例:

let myFirstPromise = new Promise((resolve, reject) => {
  // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
  // In this example, we use setTimeout(...) to simulate async code. 
  // In reality, you will probably be using something like XHR or an HTML5 API.
  setTimeout(function(){
    resolve("Success!"); // Yay! Everything went well!
  }, 250);
});  

myFirstPromise.then((successMessage) => {
  // successMessage is whatever we passed in the resolve(...) function above.
  // It doesn't have to be a string, but if it is only a succeed message, it probably will be.
  console.log("Yay! " + successMessage);
});

約束

3つの非同期関数があり、順番に実行する予定の場合は、次のようにします:

let FirstPromise = new Promise((resolve, reject) => {
    FirstPromise.resolve("First!");
});
let SecondPromise = new Promise((resolve, reject) => {

});
let ThirdPromise = new Promise((resolve, reject) => {

});
FirstPromise.then((successMessage) => {
  jQuery.ajax({
    type: "type",
    url: "url",
    success: function(response){
        console.log("First! ");
        SecondPromise.resolve("Second!");
    },
    error: function() {
        //handle your error
    }  
  });           
});
SecondPromise.then((successMessage) => {
  jQuery.ajax({
    type: "type",
    url: "url",
    success: function(response){
        console.log("Second! ");
        ThirdPromise.resolve("Third!");
    },
    error: function() {
       //handle your error
    }  
  });    
});
ThirdPromise.then((successMessage) => {
  jQuery.ajax({
    type: "type",
    url: "url",
    success: function(response){
        console.log("Third! ");
    },
    error: function() {
        //handle your error
    }  
  });  
});

このアプローチでは、必要に応じてすべての非同期操作を処理できます。

0
Iman Bahrampour

updateStatus呼び出しを別の関数に移動します。新しい関数をターゲットとしてsetTimeoutを呼び出します。

ajaxリクエストが非同期の場合、どのリクエストが完了したかを追跡するための何かが必要です。各コールバックメソッドは、それ自体のどこかに「完了」フラグを設定し、それが最後のフラグであるかどうかを確認できます。そうである場合は、hideLoaderを呼び出させます。

0
lincolnk