web-dev-qa-db-ja.com

非同期XMLHttpRequestのコールバック関数を利用するにはどうすればよいですか?

私は現在JavaScriptを書いていますcallbackについて混乱しています。しかし、組み込み関数のようなものではないことがわかりました...
O'Relly JavaScript 5th Editionを読んでいますが、以下のようなサンプルコードが表示されています。

getText = function(url, callback) // How can I use this callback?
{
    var request = new XMLHttpRequest();
    request.onreadystatechange = function()
    {
        if (request.readyState == 4 && request.status == 200)
        {
            callback(request.responseText); // Another callback here
        }
    }
    request.open('GET', url);
    request.send();
}

基本的に、callbackの一般的な考え方を理解していないと思います...上記のcallbackを利用するサンプルコードを書いてもらえますか?

23
Japboy

コールバックは非常にシンプルで気の利いたです! AJAX呼び出しの性質のため、リクエストが終わるまでdo n'tスクリプトの実行をブロックします(コールバックは、メソッドに戻ったときに応答を処理するために指定された単なるメソッドです。

Javascriptメソッドはファーストクラスのオブジェクトなので、変数のようにそれらを渡すことができます。

あなたの例では

_getText = function(url, callback) // How can I use this callback?
{
    var request = new XMLHttpRequest();
    request.onreadystatechange = function()
    {
        if (request.readyState == 4 && request.status == 200)
        {
            callback(request.responseText); // Another callback here
        }
    }; 
    request.open('GET', url);
    request.send();
}

function mycallback(data) {
   alert(data);
}

getText('somephpfile.php', mycallback); //passing mycallback as a method
_

上記を行う場合、応答(コールバック)を処理するメソッドとしてmycallbackを渡すことを意味します。

[〜#〜] edit [〜#〜]

この例では、コールバックの適切な利点を説明していませんが(結局、単にonReadyStateChange関数にアラートを置くことができます!)、再使用性は確かに要因です。

ここで重要なことは、JSメソッドがファーストクラスオブジェクトであることを覚えておく必要があります。これは、オブジェクトのようにそれらを渡し、あらゆる種類のイベントに添付できることを意味します。イベントがトリガーされると、それらのイベントに関連付けられたメソッドが呼び出されます。

request.onreadystatechange = function(){}を実行すると、適切なイベントが発生したときに呼び出されるメソッドを割り当てるだけです。

したがって、ここでの素晴らしい点は、これらのメソッドを再利用できることです。 AJAXリクエストの404の場合、アラートをポップアップし、HTMLページのいくつかのフィールドに値を設定するエラー処理メソッドがあるとします。

コールバックを割り当てたり、メソッドをパラメーターとして渡すことができなかった場合、エラー処理コードを何度も記述する必要がありますが、代わりにコールバックとして割り当てるだけで、すべてのエラー処理が並べ替えられます一度に。


47
JohnP

まず第一に、コールバックとは何かを読むことをお勧めします。 ここ は始まりです。

大きな絵

コールバックは、非同期プログラミングで広く使用されています。 (場合によっては)長時間実行される操作が完了するまでブロックしたくない場合、問題にアプローチする方法の1つは、側でそれを行う誰かに操作を委任することです。これは疑問を提起します:操作がいつ完了したかをどのように知ることができ、どのように結果を得るのでしょうか?

解決策の1つは、作業を他の人に委任し、通常の作業から時々少し時間を取り、「私があなたに与えた作業はまだ完了していますか」と尋ねることです。その場合は、何らかの方法で結果を取得し、すぐに使用できます。問題が解決しました。

このアプローチの問題は、それがあなたの人生をそれほど楽にしないということです。これで、少しずつ確認するように強制され、実際に実行されるとすぐに操作が実行されることがわかりません(ただし、次回確認することを忘れないでください)。質問するのを忘れた場合、neverは通知されません。

これに対するより良い解決策はコールバックです:仕事を委任するとき、それとともに関数を提供します。実際に作業をdoするコードは、作業が完了するとすぐにその関数を呼び出すことを約束します。これですべてを忘れて、作業が完了するとコールバックが呼び出されるという知識で安全になります。すぐに、そして後で。

ここでのコールバックは何ですか?

この特定の場合、callbackは、getTextにユーザーとの通信を許可する方法として提供する関数です。あなたは「私のためにこの仕事をしてください、そしてあなたが終わったら、ここにあなたが私に知らせるためにあなたが呼び出すための関数があります」と言っています。

getTextは実際、XMLHttpRequest(XHR)が完了したときにのみこのコールバックを使用することを選択し、同時にHTTP応答のコンテンツも「通知」します。 (したがって、その情報に基づいて行動できます)。

コールバックとその他のコールバック、ああ!

しかし、コードを読むにはもう少し時間がかかります。request.onreadystatechangeに保存される値は何ですか? request.onreadystatechange目的とは何ですか?

答えは、request.onreadystatechangeがあり、callbackを入力するためです。実際、XHRはコールバックを提供する方法を提供し、基礎となるHTTPリクエストの状態が変化するたびに「コールバック」することを約束します。

getTextは、その上に抽象化を構築する関数です:独自のコールバックをプラグインします(匿名関数-これを "inner")があり、あなたからanotherコールバックを受け入れます(パラメータ- "outer")。内側のコールバック(状態が変わるたびに呼び出される)が、状態が「完了」(値4の意味)であり、HTTP応答ステータスコードが200(「OK」を意味する)を検出したとき)、外部コールバックを呼び出して、getTextのユーザーに結果を知らせます。

私は理にかなっていると思います。 :)

17
Jon

私個人としては、コールバックよりもイベントリスナーを使用することを好みます。

リスナーを使用すると、特に複数の非同期リクエストを一度に処理する場合に便利です。

使用法は次のとおりです( https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest から取得)

function reqListener () {
  console.log(this.responseText);
}

var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send()
1
Mikolas Pansky

適切な「コールバック」方式で機能するのは、このような約束を返すサービスを定義することです!

$http.head("url2check").then(function () {
                return true;
            }, function () {
                return false;
            });

コントローラーでサービスを使用します。

<service>.<service method>.then(function (found)) {
     if (found) {......
}

@jonは非同期と呼ぶのが正しいです!

0
Sydwell

XMLHttpRequestコールバック関数とデータ配列を使用したファイルのアップロード

function HttpPost(url, arr, cb, form){
    if (form === undefined) { var data = new FormData(); }else{ var data = new FormData(form); }
    if (arr !== undefined) {
        for (const index in arr) {    
            data.append(index, arr[index]);
        }
    }
    var hr = new XMLHttpRequest();        
    hr.onreadystatechange=function(){
        if (hr.readyState==4 && hr.status==200){
            if( typeof cb === 'function' ){ cb(hr.responseText); }
        }
    }
    hr.upload.onprogress = function(e) {
        var done = e.position || e.loaded, total = e.totalSize || e.total;
        console.log('xhr.upload progress: ' + done + ' / ' + total + ' = ' + (Math.floor(done/total*1000)/10) + '%');
    };
    hr.open("POST",url,true);
    hr.send(data);
}

// HttpPost callback
function cb_list(res){
    console.log(res);
    var json = JSON.parse(res);
    console.log(json.id + ' ' + json.list);
    // loop
    for (var objindex in json.list){
        console.log(json.list[objindex].id);
    }
}

サンプル:

var data = [];
data["cmd"] = "get-cos";
var form = $('#form')[0];

HttpPost('/api-load', data, cb_list, form);

<form id="form" method="POST" enctype="multipart/form-data">
    <input type="file" name="file[]" multiple accept="image/*">
</form>

HTTPヘッダーコンテンツ

hr.setRequestHeader("Content-Type", "application/json"); 
// data:
var json = {"email": "[email protected]", "password": "101010"}
var data = JSON.stringify(json);

hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// data: 
var data = "fname=Henry&lname=Ford";
0
Dingo