Bagaimana saya bisa mendapatkan jQuery untuk melakukan permintaan Ajax yang sinkron, bukan asinkron?

1208

Saya memiliki widget JavaScript yang menyediakan titik ekstensi standar. Salah satunya adalah beforecreatefungsinya. Itu harus kembali falseuntuk mencegah barang tidak dibuat.

Saya telah menambahkan panggilan Ajax ke fungsi ini menggunakan jQuery:

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);
  });
}

Tapi saya ingin mencegah widget saya membuat item, jadi saya harus kembali falsepada fungsi ibu, bukan di callback. Apakah ada cara untuk melakukan permintaan AJAX sinkron menggunakan jQuery atau API di browser lainnya?

Artem Tikhomirov
sumber
30
Cara yang tepat untuk menyelesaikannya adalah menulis ulang janji-janji penggunaan titik ekstensi widget Anda. Dengan cara ini Anda dapat dengan mudah mengatur tindakan asinkron (seperti permintaan ajax) sebagai beforecreate.
Kos
3
Saya mengusulkan fungsi Sajak. Apakah kita punya huruf T? Ya vanna, beri aku 3 T.
Cody O'Dell
2
@ Ko tepat. Sinkronisasi XHR tidak diperlukan di sini
Michael Westcott
1
Jika orang meminta saya untuk 1 saran ketika memulai javascript, saya katakan: merangkul karakter asinkron dari javascript, jangan coba-coba melawannya.
Emmanuel Delay
3
Pertanyaan ini seperti menanyakan "bagaimana cara menyimpan kata sandi pengguna saya dalam teks biasa?" Tentu ada jawaban, tapi jangan lakukan itu.
Vectorjohn

Jawaban:

1165

Dari dokumentasi jQuery : Anda menentukan opsi asynchronous menjadi false untuk mendapatkan permintaan Ajax yang sinkron. Kemudian panggilan balik Anda dapat mengatur beberapa data sebelum fungsi ibumu berlanjut.

Ini akan terlihat seperti apa kode Anda jika diubah seperti yang disarankan:

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
    });
}
Adam Bellaire
sumber
97
Tepatnya, tidak mungkin menggunakan get (), post (), load () untuk panggilan sinkron. Hanya ajax () yang memiliki parameter "async", yang dapat disetel ke "false".
SLA80
39
@ SLA80 Tidak. Sejak jQuery 1.1: stackoverflow.com/questions/6849686/…
StuperUser
16
@ equalidafial komentar saya ditujukan pada komentar SLA80 yang salah; dimungkinkan untuk menggunakan get (), post (), load () untuk panggilan sinkron.
StuperUser
9
async false tidak lagi didukung untuk jQuery> = 1.8. Lihat api.jquery.com/jQuery.ajax
ken
22
@ken async: falsemasih didukung di jQuery> = 1.8. Kemampuan untuk digunakan async: falsedengan jqXHR ( $.Deferred) adalah apa yang sudah usang. Dengan kata lain, seseorang harus menggunakan opsi keberhasilan / kesalahan / panggilan balik yang lebih baru, daripada metode sebelumnya, misalnya jqXHR.done(),.
Ben Johnson
255

Anda dapat menempatkan pengaturan Ajax jQuery dalam mode sinkron dengan memanggil

jQuery.ajaxSetup({async:false});

Dan kemudian lakukan panggilan Ajax Anda menggunakan jQuery.get( ... );

Kemudian cukup nyalakan sekali lagi

jQuery.ajaxSetup({async:true});

Saya kira itu bekerja sama dengan yang disarankan oleh @Adam, tetapi mungkin bermanfaat bagi seseorang yang ingin mengkonfigurasi ulang jQuery.get()atau sintaks yang jQuery.post()lebih rumit jQuery.ajax().

Sydwell
sumber
56
Ini adalah ide yang sangat buruk, mengapa Anda menetapkannya di tingkat global? Dan kemudian dipaksa untuk membatalkannya setelah?
Juan Mendes
11
Setidaknya, ini adalah ide buruk terbaik. alih-alih mengatakan: "TIDAK ADA CARA KECUALI $.ajax()". ;)
Rzassar
136

Solusi yang sangat baik! Saya perhatikan ketika saya mencoba menerapkannya bahwa jika saya mengembalikan nilai dalam klausa keberhasilan, itu kembali sebagai tidak terdefinisi. Saya harus menyimpannya dalam variabel dan mengembalikan variabel itu. Ini adalah metode yang saya buat:

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;
}
James di Indy
sumber
16
Itu karena Anda mengembalikan nilai dari callback, bukan dari getWhatever. Jadi Anda tidak mengembalikan apa pun yaitu undefineddari Anda getWhatever.
Lightness Races in Orbit
6
ini benar-benar salah. Anda tidak dapat memastikan bahwa panggilan tidak sinkron selesai ketika mengembalikan 'strReturn'.
Thomas Fritz
61
Ini adalah panggilan Sinkron (async: false).
James di Indy
85

Semua jawaban ini kehilangan titik bahwa melakukan panggilan Ajax dengan async: false akan menyebabkan browser hang sampai permintaan Ajax selesai. Menggunakan pustaka kontrol aliran akan menyelesaikan masalah ini tanpa menutup browser. Berikut ini adalah contoh dengan 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();
}
BishopZ
sumber
4
Panggilan sinkron berguna jika Anda ingin mengumpulkan uji coba cepat untuk back-end REST dan lebih suka kesederhanaan daripada panggilan balik neraka.
Distortum
50
function getURL(url){
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}


//example use
var msg=getURL("message.php");
alert(msg);
Carcione
sumber
10
Perhatikan bahwa meskipun, Anda mungkin mendapatkan ini: 'XMLHttpRequest Synchronous di utas utama sudah usang karena efeknya yang merugikan pengalaman pengguna akhir.'
Hari
5
@Hari Apa cara untuk melakukan panggilan ajax sinkron menggunakan jQuery tanpa menggunakan utas?
Jose Nobile
7
Saya tidak melihat bahwa jawaban ini memiliki sesuatu untuk ditawarkan, itu hanya jawaban yang diterima yang dibungkus dengan fungsi dan dijawab empat tahun kemudian. Adalah hal yang buruk untuk menyarankan orang ke C + P, karena fungsinya tidak menunjukkan apa yang dilakukannya. "BegitugetURL vs get? Mengapa kita menggantung browser saya?" dll
moopet
27

Catatan: Anda tidak boleh menggunakan async: falsekarena pesan peringatan ini:

Dimulai dengan Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), permintaan sinkron pada utas utama telah ditinggalkan karena efek negatif terhadap pengalaman pengguna.

Chrome bahkan memperingatkan tentang hal ini di konsol:

Sinkronisasi XMLHttpRequest pada utas utama sudah usang karena efeknya yang merugikan bagi pengalaman pengguna akhir. Untuk bantuan lebih lanjut, periksa https://xhr.spec.whatwg.org/ .

Ini dapat merusak halaman Anda jika Anda melakukan sesuatu seperti ini karena itu bisa berhenti bekerja setiap hari.

Jika Anda ingin melakukannya dengan cara yang masih terasa seperti itu sinkron tetapi masih tidak memblokir maka Anda harus menggunakan async / tunggu dan mungkin juga beberapa ajax yang didasarkan pada janji-janji seperti yang baru Ambil API baru

async function foo() {
  var res = await fetch(url)
  console.log(res.ok)
  var json = await res.json()
  console.log(json)
}

Sunting chrome bekerja pada Menonaktifkan sinkronisasi XHR dalam pemecatan halaman saat halaman sedang dinavigasi atau ditutup oleh pengguna. Ini melibatkan sebelumunload, unload, menyembunyikan halaman dan perubahan visibilitas.

jika ini adalah use case Anda maka Anda mungkin ingin melihat navigator.sendBeacon sebagai gantinya

Dimungkinkan juga untuk menonaktifkan req sinkronisasi dengan header http atau atribut iframe yang memungkinkan

Tak berujung
sumber
Perhatikan bahwa Anda harus membungkus await fetch(url)panggilan Anda di try... catchblok untuk menangkap kesalahan asinkron.
inostia
4
fungsi foo telah dikonversi menjadi janji dengan hanya menggunakan kata asyncdi awal, jadi Anda bisa melakukannya foo().catch(...)untuk menangkapnya juga
Endless
Chrome sudah melarang async ajax di dalam halaman beforeunloadtetapi kemudian mengembalikan perubahan setelah umpan balik negatif bugs.chromium.org/p/chromium/issues/detail?id=952452
Alex
26

Perlu diingat bahwa jika Anda melakukan panggilan Ajax lintas-domain (dengan menggunakan JSONP ) - Anda tidak dapat melakukannya secara sinkron, asyncbendera akan diabaikan oleh jQuery.

$.ajax({
    url: "testserver.php",
    dataType: 'jsonp', // jsonp
    async: false //IGNORED!!
});

Untuk panggilan JSONP Anda bisa menggunakan:

  1. Ajax-panggilan ke domain Anda sendiri - dan lakukan sisi server panggilan lintas-domain
  2. Ubah kode Anda untuk bekerja secara tidak sinkron
  3. Gunakan pustaka "sequencer fungsi" seperti Frame.js ( jawaban ini )
  4. Blokir UI alih-alih menghalangi eksekusi ( jawaban ini ) (cara favorit saya)
Serge Shultz
sumber
12

Dengan async: falseAnda mendapatkan sendiri browser yang diblokir. Untuk solusi sinkron yang tidak menghalangi Anda dapat menggunakan yang berikut:

ES6 / ECMAScript2015

Dengan ES6 Anda dapat menggunakan generator & perpustakaan bersama :

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

Dengan ES7 Anda bisa menggunakan asyc menunggu:

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
    })(); 
}
Spenhouet
sumber
3
Perhatikan Anda bisa langsung async functionmenjadi bereecreate beforecreate: async function(....dan menghapus fungsi async yang dipanggil sendiri
Daniel Krom
12

Saya menggunakan jawaban yang diberikan oleh Carcione dan memodifikasinya untuk menggunakan 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');
    }    
}

Saya menambahkan dataType dari 'JSON' dan mengubah .responseText untuk responseJSON .

Saya juga mengambil status menggunakan properti statusText dari objek yang dikembalikan. Perhatikan, bahwa ini adalah status respons Ajax, bukan apakah JSON valid.

Bagian belakang harus mengembalikan respons dalam JSON yang benar (terbentuk dengan baik), jika tidak objek yang dikembalikan akan tidak terdefinisi.

Ada dua aspek yang harus dipertimbangkan ketika menjawab pertanyaan awal. Seseorang memberitahu Ajax untuk melakukan secara sinkron (dengan mengatur async: false ) dan yang lainnya mengembalikan respons melalui pernyataan pengembalian fungsi panggilan, alih-alih menjadi fungsi panggilan balik.

Saya juga mencobanya dengan POST dan berhasil.

Saya mengubah GET menjadi POST dan menambahkan data: 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;
}

Perhatikan bahwa kode di atas hanya bekerja dalam kasus di mana async adalah palsu . Jika Anda menetapkan async: true objek yang dikembalikan jqxhr tidak akan valid pada saat panggilan AJAX kembali, hanya nanti ketika panggilan asinkron telah selesai, tetapi itu terlalu terlambat untuk mengatur variabel respons .

paulo62
sumber
7

Ini adalah contoh:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});
mencari9x
sumber
1
Apa gunanya menggunakan async falsedengan solusi berbasis janji. Ini hanya mengunci browser tanpa manfaat sama sekali.
Hilang Coding
2
@GoneCoding ada situasi di mana ingin mengeksekusi kode dalam urutan yang tepat, dalam situasi itu kita dapat menggunakan async false
Nikki
1
@ Nikki: Itu sama sekali bukan cara untuk menjalankannya secara berurutan. Gunakan janji async ( ajaxpanggilan yang sudah kembali) dan .then()atau done()(seperti yang sudah ditunjukkan). Mengunci browser adalah pengalaman pengguna yang sangat buruk. Seperti yang saya katakan memiliki async: falsedalam contoh ini adalah buang-buang waktu.
Hilang Pengkodean
4

Pertama-tama kita harus mengerti kapan kita menggunakan $ .ajax dan kapan kita menggunakan $ .get / $. Post

Ketika kita membutuhkan kontrol level rendah atas permintaan ajax seperti pengaturan header permintaan, pengaturan caching, pengaturan sinkron dll. Maka kita harus mencari $ .ajax.

$ .get / $. post: Ketika kita tidak memerlukan kontrol level rendah atas permintaan ajax. Hanya sederhana dapatkan / posting data ke server. Ini adalah singkatan

$.ajax({
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

dan karenanya kami tidak dapat menggunakan fitur lain (sinkronisasi, cache, dll.) dengan $ .get / $. post.

Karenanya untuk kontrol level rendah (sinkronisasi, cache, dll.) Atas permintaan ajax, kita harus menggunakan $ .ajax

 $.ajax({
     type: 'GET',
      url: url,
      data: data,
      success: success,
      dataType: dataType,
      async:false
    });
Sheo Dayal Singh
sumber
2

ini adalah implementasi sederhana saya untuk permintaan ASYNC dengan jQuery. Saya harap ini membantu siapa pun.

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();
Felipe Marques
sumber
2

Karena XMLHttpReponseoperasi sinkron sudah usang saya datang dengan solusi berikut yang membungkus XMLHttpRequest. Ini memungkinkan permintaan AJAX yang dipesan sementara masih bersifat asiknron, yang sangat berguna untuk token CSRF sekali pakai.

Ini juga transparan sehingga perpustakaan seperti jQuery akan beroperasi dengan lancar.

/* 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;
};
Geoffrey
sumber
-1

Karena pertanyaan aslinya adalah tentang jQuery.get, perlu disebutkan di sini bahwa (seperti yang disebutkan di sini ) orang dapat menggunakan async: falsedalam $.get()tetapi idealnya menghindarinya karena asinkron XMLHTTPRequestsudah usang (dan browser dapat memberikan peringatan):

$.get({
  url: url,// mandatory
  data: data,
  success: success,
  dataType: dataType,
  async:false // to make it synchronous
});
Anupam
sumber
1
mengapa ini diturunkan?
alias51
-2

Setel asyn:falsedalam permintaan ajax untuk membuatnya sinkron.

Berikut ini contoh kode:

 $.ajax({
    url: 'some_url',
    type: "GET",
    async: false,
    cache: false,
    dataType: "JSON",
    data: { 
      "p1": v1, 
      "p2": v2
    },
    success: function(result) {
    }  
  }
});

Ini dapat membuat layar tidak responsif.

Usman Afzal
sumber