非同期のAjaxリクエストではなく、jQueryに同期を実行させるにはどうすればいいですか?

9527 ワード

標準的な拡張ポイントを提供するJavaScriptウィジェットがあります.そのうちの1つはbeforecreate関数です.プロジェクトの作成を防ぐためにfalseに戻る必要があります.
私はすでにjQueryを使ってこの関数にAjax呼び出しを追加しました:
beforecreate: function (node, targetNode, type, to) {
  jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),

  function (result) {
    if (result.isOk == false) 
        alert(result.message);
  });
}

しかし、私のコンポーネントがプロジェクトを作成することを防止したいので、コールバックでfalseを返すのではなく、親関数に戻るべきです.jQueryまたは他のブラウザ内のAPIを使用して同期AJAXリクエストを実行する方法はありますか?
1階
function getURL(url){
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}


//example use
var msg=getURL("message.php");
alert(msg);

2階
これらの答えはすべて、async:falseを使用してAjax呼び出しを行うと、Ajaxリクエストが完了するまでブラウザが停止します.フロー制御ライブラリを使用すると、ブラウザを停止することなく、この問題が解決します.これはFrame.jsの例です.
beforecreate: function(node,targetNode,type,to) {

    Frame(function(next)){

        jQuery.get('http://example.com/catalog/create/', next);
    });

    Frame(function(next, response)){

        alert(response);
        next();
    });

    Frame.init();
}

#3階
jQueryドキュメントから:同期Ajaxリクエストを取得するために、非同期オプションをfalseとして指定します.その後、コールバック関数は、親関数が実行される前にデータを設定できます.
推奨に従って変更すると、コードは次のようになります.
beforecreate: function (node, targetNode, type, to) {
    jQuery.ajax({
        url: 'http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),
        success: function (result) {
            if (result.isOk == false) alert(result.message);
        },
        async: false
    });
}

#4階
これは例です.
$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});

#5階
優れたソリューション!実装しようとすると、成功した句に値を返すと、未定義の状態が返されることに気づきました.変数に保存して返さなければなりません.これは私が考えた方法です.
function getWhatever() {
  // strUrl is whatever URL you need to call
  var strUrl = "", strReturn = "";

  jQuery.ajax({
    url: strUrl,
    success: function(html) {
      strReturn = html;
    },
    async:false
  });

  return strReturn;
}

#6階
Carcioneが出した答えを使ってJSONを使うように修正しました.
 function getUrlJsonSync(url){

    var jqxhr = $.ajax({
        type: "GET",
        url: url,
        dataType: 'json',
        cache: false,
        async: false
    });

    // 'async' has to be 'false' for this to work
    var response = {valid: jqxhr.statusText,  data: jqxhr.responseJSON};

    return response;
}    

function testGetUrlJsonSync()
{
    var reply = getUrlJsonSync("myurl");

    if (reply.valid == 'OK')
    {
        console.dir(reply.data);
    }
    else
    {
        alert('not valid');
    }    
}

「JSON」のデータ型を追加し、.responseTextをresponseJSONに変更しました.
また、戻りオブジェクトのstatusTextプロパティを使用してステータスを取得しました.JSONが有効かどうかではなく、Ajax応答の状態であることに注意してください.
バックエンドは、正しい(フォーマットが正しい)JSONで応答を返さなければなりません.そうしないと、返されるオブジェクトは不確定になります.
元の質問に答えるには、2つの側面を考慮する必要があります.1つはAjaxに同期実行(async:falseを設定することによって)を伝えることであり、もう1つはコールバック関数ではなく関数のreturn文を呼び出すことによって応答を返すことである.
私もPOSTで試してみましたが、有効でした.
GETをPOSTに変更してデータを追加しました:postdata
function postUrlJsonSync(url, postdata){

    var jqxhr = $.ajax({
        type: "POST",
        url: url,
        data: postdata,
        dataType: 'json',
        cache: false,
        async: false
    });

    // 'async' has to be 'false' for this to work
    var response = {valid: jqxhr.statusText,  data: jqxhr.responseJSON};

    return response;
}

上記のコードはasyncがfalseの場合にのみ有効であることに注意してください.async:trueを設定する場合、返されるオブジェクトjqxhrはAJAX呼び出しの戻り時に無効になり、非同期呼び出しが完了した後にのみ有効になりますが、これは遅すぎて応答変数を設定できません.
#7階
ドメイン間Ajax呼び出し(JSONPを使用)を実行している場合-同期できない場合は、jQueryはasyncフラグを無視します.
$.ajax({
    url: "testserver.php",
    dataType: 'jsonp', // jsonp
    async: false //IGNORED!!
});

JSONPコールの場合は、次の操作を行います.
  • Ajax独自のドメインを呼び出し、サーバー側でドメイン間呼び出し
  • を行います.
  • 非同期で動作するようにコードを変更する
  • Frame.jsのような「関数シーケンサ」ライブラリ(この答え)
  • を使用
  • 実行を阻止するのではなくUIを阻止する(この答え)(私の最も好きな方法)
  • #8階async: falseを使用すると、ブロックされたブラウザが表示されます.非ブロッキング同期ソリューションでは、次の方法を使用できます.
    ES6/ECMAScript2015
    ES 6を使用すると、generatorとcoライブラリを使用できます.
    beforecreate: function (node, targetNode, type, to) {
        co(function*(){  
            let result = yield jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
            //Just use the result here
        });
    }
    

    ES7
    ES 7を使用すると、asyc awaitのみを使用できます.
    beforecreate: function (node, targetNode, type, to) {
        (async function(){
            let result = await jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
            //Just use the result here
        })(); 
    }
    

    #9階
    注:次の警告メッセージのため、async: falseを使用しないでください.
    Gecko 30.0(Firefox 30.0/Thunderbird 30.0/SeaMonkey 2.27)から、プライマリスレッド上の同期要求は、ユーザエクスペリエンスに悪影響を及ぼすため廃棄された.
    Chromeはコンソールで警告することもあります.
    エンドユーザーの体験に悪影響を及ぼすため、同期XMLHttpRequestをプライマリ・スレッドで使用することには賛成しません.詳細については、https://xhr.spec.whatwg.org/.
    このような操作を実行している場合は、一日で作業を停止する可能性があるため、ページが中断される可能性があります.
    まだ同期しているがブロックされない方法でこの操作を実行する場合は、async/awaitを使用し、新しいFetch APIなどのPromiseベースのajaxを使用することもできます.
    async function foo() {
      var res = await fetch(url)
      console.log(res.ok)
      var json = await res.json()
      console.log(json)
    }
    

    ユーザーがページを移動または閉じるとき、Edit chromeは「ページを閉じるときにXHRを同期させることを禁止する」ことを処理しています.これは、アンインストール、アンインストール、ページの非表示、可視性の変更に関連します.
    これが使用例であれば、navigator.sendBeaconではなくnavigator.sendBeaconを見たいかもしれません.
    ページでは、HTTPヘッダまたはiframe付きallowプロパティの同期要求も無効になる場合があります.
    #10階
    まず、$.ajaxをいつ使用するか、$.get/$をいつ使用するかを理解する必要があります.post
    ajaxリクエストを低レベルに制御する必要がある場合、ヘッダ設定、キャッシュ設定、同期設定などを要求する場合は、$.ajaxを選択します.
    $ .get/$.post:ajaxリクエストを低レベルに制御する必要がない場合は、データをサーバに簡単に取得/パブリッシュするだけです.はい、そうです.
    $.ajax({
      url: url,
      data: data,
      success: success,
      dataType: dataType
    });
    

    したがって、$.get/$では使用できません.postでは他の機能(同期、キャッシュなど)を使用します.
    したがって、ajaxリクエストに対する低レベル制御(同期、キャッシュなど)については、$.ajaxを使用する必要があります.
     $.ajax({
         type: 'GET',
          url: url,
          data: data,
          success: success,
          dataType: dataType,
          async:false
        });
    

    #11階
    これは私のjQueryに対するASYNCリクエストの簡単な実装です.誰にでも役に立つことを願っています.
    var queueUrlsForRemove = [
        'http://dev-myurl.com/image/1', 
        'http://dev-myurl.com/image/2',
        'http://dev-myurl.com/image/3',
    ];
    
    var queueImagesDelete = function(){
    
        deleteImage( queueUrlsForRemove.splice(0,1), function(){
            if (queueUrlsForRemove.length > 0) {
                queueImagesDelete();
            }
        });
    
    }
    
    var deleteImage = function(url, callback) {
        $.ajax({
            url: url,
            method: 'DELETE'
        }).done(function(response){
            typeof(callback) == 'function' ? callback(response) : null;
        });
    }
    
    queueImagesDelete();
    

    #12階XMLHttpReponseの同時運用には賛成しないので、以下のパッケージXMLHttpRequestソリューションを提案しました.これは秩序化されたAJAXクエリを可能にするが,本質的には非同期であり,これは使い捨てCSRFトークンにとって非常に有用である.
    これも透明なので、jQueryなどのライブラリはシームレスに実行されます.
    /* wrap XMLHttpRequest for synchronous operation */
    var XHRQueue = [];
    var _XMLHttpRequest = XMLHttpRequest;
    XMLHttpRequest = function()
    {
      var xhr   = new _XMLHttpRequest();
      var _send = xhr.send;
    
      xhr.send = function()
      {
        /* queue the request, and if it's the first, process it */
        XHRQueue.push([this, arguments]);
        if (XHRQueue.length == 1)
          this.processQueue();
      };
    
      xhr.processQueue = function()
      {
        var call = XHRQueue[0];
        var xhr  = call[0];
        var args = call[1];
    
        /* you could also set a CSRF token header here */
    
        /* send the request */
        _send.apply(xhr, args);
      };
    
      xhr.addEventListener('load', function(e)
      {
        /* you could also retrieve a CSRF token header here */
    
        /* remove the completed request and if there is more, trigger the next */
        XHRQueue.shift();
        if (XHRQueue.length)
          this.processQueue();
      });
    
      return xhr;
    };
    

    #13階
    これは私の「Thread」ネームスペースです.
    var Thread = {
        sleep: function(ms) {
            var start = Date.now();
    
            while (true) {
                var clock = (Date.now() - start);
                if (clock >= ms) break;
            }
    
        }
    };
    

    これが私が言ったことです.
    var d1 = new Date();
    console.log('start ' + d1.toLocaleTimeString());
    Thread.sleep(10000);
    var d2 = new Date();
    console.log('end ' + d2.toLocaleTimeString());
    

    コンソールを表示すると、次の結果が得られます.
    start 1:41:21 PM
    end 1:41:31 PM
    

    #14階
    呼び出しでjQueryのAjax設定を同期モードにすることができます
    jQuery.ajaxSetup({async:false});
    

    次に、jQuery.get( ... );を使用してAjax呼び出しjQuery.get( ... );を実行する
    そしてもう一度
    jQuery.ajaxSetup({async:true});
    

    その動作原理は@Adamが提案したのと同じだと思いますが、jQuery.get()またはjQuery.post()をより複雑なjQuery.ajax()文法に再構成したい人には役に立つかもしれません.