0

script tag long pollingを使用して、クロスサイト コメット http サーバー プッシュ メカニズムを実装する必要があります。(ふぅ...) このために、動的にスクリプト タグを DOM に挿入すると、サーバーは短い js スクリプトを送り返します。このスクリプトは、着信メッセージを処理するローカル コールバック関数を呼び出すだけです。これらのコールバック呼び出しのそれぞれを、それを送信したスクリプト タグに関連付けて、着信応答と対応する要求を一致させる方法を見つけようとしています。

明らかに、単純にリクエスト ID を GET URL に含めて、それをサーバーが生成する js スクリプトに返すこともできますが、これでは大量の不要なトラフィックが発生し、特に洗練されている、または賢いとは思えません。

私がやりたいことは、リクエスト ID を生成したスクリプト タグに何らかの方法で関連付けてから、このスクリプト タグ内から呼び出されるコールバック関数内からこのリクエスト ID を読み取ることです。そうすれば、すべてのリクエスト管理はクライアントに残ります。

これは、次の質問につながります:現在実行中のスクリプト タグの DOM 要素をブラウザーに要求する方法はありますか?

このスレッドを見つけました:

現在実行中の動的に追加されたスクリプト タグの取得

これはまさにこの質問をしていますが、サーバーから返されたjsスクリプト(スクリプト内でマーカー変数を設定する)で膨張が必要であり、スクリプトの一意のファイル名に依存しているため、受け入れられた回答は役に立ちません。持っていません。

また、このスレッドは関連しています:

現在実行中のスクリプトをロードしたスクリプト タグを参照するにはどうすればよいですか?

また、DOM 内の最後のスクリプトは順番に実行されるため、単純に取得することをお勧めします。ただし、これはページの読み込み中にのみ機能するようであり、スクリプトが動的に追加され、挿入とは無関係な順序で読み込みが完了するシナリオでは機能しないようです。

何かご意見は?

PS:クライアントのみのソリューションを探しています。つまり、リクエスト ID や一意のコールバック関数名、またはサーバーに送信して処理する必要があるその他の非ペイロード データはありません。サーバーが (理論的には) 2 つの 100% 同一のスクリプトを返すことができ、クライアントがそれらを正しく関連付けることができるようにしたいと考えています。

4

4 に答える 4

0

非常に単純なはずです。サーバーの「接続」ごとにスクリプト要素は1つだけであり、スコープ付きの静的変数に簡単に格納できます。

function connect(nameOfCallback, eventCallback) {
    var script;
    window[nameOfCallback] = function() { // this is what the response invokes
        reload();
        eventCallback.call(null, arguments);
    };
    reload();

    function reload() {
        if (script && script.parentNode)
            script.parentNode.removeChild(script);
        script = document.createElement(script);
        script.src = "…";
        script.type = "text/javascript";
        document.head.appendChild(script);
        // you might use additional error handling, e.g. something like
        // script.onerror = reload;
        // but I guess you get the concept
    }
}
于 2012-10-14T00:08:22.900 に答える
0

アプローチの変更についての議論を避けたいと思っていることは知っていますが、それは本当にあなたがしなければならないことです.

まず、ポーリング リクエストを開始するために DOM に追加される各スクリプト タグは破棄可能です。つまり、それぞれのタグは、目的が果たされたらすぐに DOM から削除する必要があります。そうしないと、クライアント DOM が数百以上のデッド スクリプト タグで溢れてしまうことになります。

これがどのように機能するかを示す良い比較例は、jsonp 実装です。クライアント側の名前付き関数を作成し、スクリプト タグを作成してリモート リクエストを作成し、リクエストで関数名を渡します。応答スクリプトは、関数呼び出しで json オブジェクトをその名前でラップします。これにより、戻り時に関数が実行され、json ペイロードが関数に渡されます。実行後、クライアント側関数は削除されます。jQuery は、ランダムに生成された名前を作成することによってこれを行い (名前はグローバル コンテキストに存在し、これが実際にこのプロセスが機能する唯一の方法です)、完了したらコールバック関数を削除します。

ロングポーリングに関しては、非常によく似たプロセスです。本質的に、応答関数の呼び出しは、それを開始したスクリプト タグを知る必要も気にする必要もありません。

スクリプトの例を見てみましょう。

window.callback = function(obj){
    console.log(obj);   
}

setInterval(function(){

    var remote = document.createElement('script');
    remote.src = 'http://jsonip.com/callback';
    remote.addEventListener('load', function(){
        remote.parentNode.removeChild(remote);
    },false);

    document.querySelector('head').appendChild(remote);

}, 2000);​

このスクリプトは、使い捨てであるため、スクリプト要素への参照を保持しません。彼らの仕事が終わるとすぐに、彼らは即座に撃たれます。

この例を少し変更して setInterval を使用しないようにすることもできます。その場合は、setInterval を名前付き関数に置き換え、リモート ロード イベントにロジックを追加して、ロード イベントの完了時に関数をトリガーします。そうすれば、スクリプト タグ イベント間のタイミングはサーバーの応答時間に依存し、実際の長いポーリング プロセスにより近くなります。

キューイング システムを使用してコールバックを管理することで、これをさらに拡張できます。これは、戻ってくるさまざまな種類のデータに対応するさまざまな関数がある場合に役立ちます。

あるいは、おそらくより良い方法は、各ポーリングから返されたデータを処理し、その時点で他の特定のクライアント側ロジックを実行するコールバック関数にログインすることです。これは、必要なコールバック関数が 1 つだけであり、ランダムに生成されたコールバック名を作成する必要がなくなることも意味します。

これについてさらに支援が必要な場合は、具体的な質問をコメントに残してください。詳細を説明できます。

于 2012-10-13T23:35:28.580 に答える
0

スクリプトで onload/onreadystate イベントを使用する解決策があるかもしれません。これらのイベントを、リクエスト ID を持つクロージャ関数に渡すことができます。次に、コールバック関数はサーバーの応答をすぐには処理せず、グローバル変数に格納します。次に、onload/onreadystate ハンドラーは、最後に保存された応答を取得し、認識している要求 ID でタグ付けしてから、応答を処理します。

これが機能するには、イベントの順序に依存できる必要があります。対応する script タグの実行が終了した直後にonload が常に実行される場合、これはうまく機能します。しかし、2 つのタグが同時に読み込まれ、それらが同時に返され、ブラウザが両方を実行し、その後両方の onload/onreadystate イベントを実行する可能性がある場合、この方法で 1 つの応答が失われます。

誰かがこれについて何か洞察を持っていますか?

.

これを示すコードを次に示します。

function loadScript(url, requestID) {
  var script = document.createElement('script');
  script.setAttribute("src", url);
  script.setAttribute("type", "text/javascript");
  script.setAttribute("language", "javascript");
  script.onerror = script.onload = function() {
    script.onerror = script.onload = script.onreadystatechange = function () {}
    document.body.removeChild(script);
    completeRequest(requestID);
  }
  script.onreadystatechange = function () {
    if (script.readyState == 'loaded' || script.readyState == 'complete') {
      script.onerror = script.onload = script.onreadystatechange = function () {}
      document.body.removeChild(script);
      completeRequest(requestID);
    }
  }
  document.body.appendChild(script);
}

var lastReply;

function myCallback(reply) {
  lastReply = reply;
}

function completeRequest(requestID) {
  processReply(requestID, lastReply);
}

function processReply(requestID, reply) {
  // Do something
}

現在、サーバーは単純に次の形式のスクリプトを返します。

myCallback(message);

リクエスト ID などを気にする必要がなく、常に同じコールバック関数を使用できます。

問題は、「同時に」返す 2 つのスクリプトがある場合、次の呼び出し順序になる可能性があるということです。

myCallback(message1);
myCallback(message2);
completeRequest(requestID1);
completeRequest(requestID2);

もしそうなら、私はリクエスト 1 への実際の返信を失い、リクエスト 2 への返信をリクエスト 1 に誤って関連付けます。

于 2012-10-13T23:43:42.293 に答える
0

それは間違いなく可能ですが、少しコツが必要です。これはJSONPとして知られる一般的な手法です。

JavaScript の場合:

var get_a_unique_name = (function () {
    var counter = 0;
    return function () {
        counter += 1;
        return "function_" + counter;
    }
}()); // no magic, just a closure

var script = document.createElement("script");
var callback_name = get_a_unique_name();
script.src = "/request.php?id=12345&callback_name=" + callback_name;

// register the callback function globally
window[callback_name] = function (the_data) {
    console.log(the_data);
    handle_data(the_data); // implement this function
};

// add the script
document.head.appendChild(script);

あなたが持つことができるサーバー側:

$callback_name = $_GET["callback_name"];
$the_data = handle_request($_GET["id"]); // implement handle_request
echo $callback_name . "(" . json_encode($the_data) . ");";
exit; // done

によって返されるスクリプトは次の/request.php?id=12345&callback_name=XXXようになります。

function_0({ "hello": "world", "foo" : "bar" });
于 2012-10-13T23:31:09.563 に答える