jQuery ajax (jsonp) mengabaikan waktu tunggu dan tidak mengaktifkan peristiwa kesalahan

89

Untuk menambahkan beberapa penanganan kesalahan dasar, saya ingin menulis ulang sepotong kode yang menggunakan $ .getJSON jQuery untuk menarik beberapa foto dari Flickr. Alasan untuk melakukan ini adalah karena $ .getJSON tidak menyediakan penanganan kesalahan atau bekerja dengan waktu tunggu.

Karena $ .getJSON hanyalah pembungkus sekitar $ .ajax saya memutuskan untuk menulis ulang hal itu dan kejutan kejutan, itu bekerja dengan sempurna.

Sekarang kesenangan dimulai. Ketika saya dengan sengaja menyebabkan 404 (dengan mengubah URL) atau menyebabkan jaringan menjadi waktu tunggu (dengan tidak terhubung ke interwebs), peristiwa kesalahan tidak menyala, sama sekali. Saya bingung dengan apa yang saya lakukan salah. Bantuan sangat dihargai.

Berikut kodenya:

$(document).ready(function(){

    // var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne"; // correct URL
    var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne_______"; // this should throw a 404

    $.ajax({
        url: jsonFeed,
        data: { "lang" : "en-us",
                "format" : "json",
                "tags" : "sunset"
        },
        dataType: "jsonp",
        jsonp: "jsoncallback",
        timeout: 5000,
        success: function(data, status){
            $.each(data.items, function(i,item){
                $("<img>").attr("src", (item.media.m).replace("_m.","_s."))
                          .attr("alt", item.title)
                          .appendTo("ul#flickr")
                          .wrap("<li><a href=\"" + item.link + "\"></a></li>");
                if (i == 9) return false;
            });
        },
        error: function(XHR, textStatus, errorThrown){
            alert("ERREUR: " + textStatus);
            alert("ERREUR: " + errorThrown);
        }
    });

});

Saya ingin menambahkan bahwa pertanyaan ini diajukan ketika jQuery berada di versi 1.4.2

Matijs
sumber

Jawaban:

86

jQuery 1.5 dan yang lebih tinggi memiliki dukungan yang lebih baik untuk penanganan kesalahan dengan permintaan JSONP. Namun, Anda perlu menggunakan $.ajaxmetode ini, bukan $.getJSON. Bagi saya, ini berhasil:

var req = $.ajax({
    url : url,
    dataType : "jsonp",
    timeout : 10000
});

req.success(function() {
    console.log('Yes! Success!');
});

req.error(function() {
    console.log('Oh noes!');
});

Waktu tunggu tampaknya berhasil dan memanggil penangan kesalahan, ketika tidak ada permintaan yang berhasil setelah 10 detik.

Saya membuat posting blog kecil tentang subjek ini juga.

Serak
sumber
23
Saya sangat kesal sekarang karena saya telah membenturkan kepala terhadap masalah ini selama 2 hari dan dibutuhkan beberapa pencarian yang tidak jelas di StackOverflow untuk mengetahui bahwa saya harus menambahkan batas waktu ke permintaan lintas domain untuk membuat kesalahan diaktifkan. Bagaimana ini tidak disebutkan dalam dokumentasi jQuery? Apakah permintaan jsonp lintas domain benar-benar tidak umum? Bagaimanapun, terima kasih, terima kasih, terima kasih. +1
chrixian
Dengan solusi ini, saya tidak bisa mendapatkan kode status, saya malah mendapatkan "batas waktu". Jadi saya tidak bisa tahu apa kesalahan sebenarnya, dan tidak bisa mengatasinya properti.
Tarlog
10
@Tarlog: itu logis. Permintaan JSONP bukanlah permintaan Ajax asli, itu hanyalah <script>tag Javascript yang disisipkan secara dinamis. Oleh karena itu, satu-satunya hal yang dapat Anda ketahui adalah bahwa permintaan gagal, bukan kode statusnya. Jika Anda ingin mendapatkan kode status, Anda perlu menggunakan permintaan Ajax tradisional atau teknik lintas-domain lainnya.
Husky
1
Seperti yang dikatakan @Husky, Anda tidak dapat memiliki kode status dengan JSONP, dan saya yakin itulah mengapa Anda harus mencoba menghindarinya. Satu-satunya cara untuk mendapatkan kesalahan adalah dengan memberi waktu tunggu. Ini harus lebih baik dijelaskan dalam dokumentasi jQuery (seperti banyak hal lainnya).
Alejandro García Iglesias
67

Ini adalah batasan yang diketahui dengan implementasi jsonp asli di jQuery. Teks di bawah ini berasal dari IBM DeveloperWorks

JSONP adalah teknik yang sangat ampuh untuk membangun mashup, tetapi, sayangnya, ini bukan solusi untuk semua kebutuhan komunikasi lintas domain Anda. Ada beberapa kekurangan yang harus dipertimbangkan secara serius sebelum menggunakan sumber daya pembangunan. Pertama dan terpenting, tidak ada penanganan error untuk panggilan JSONP. Jika penyisipan skrip dinamis berfungsi, Anda akan dipanggil; jika tidak, tidak ada yang terjadi. Itu hanya gagal secara diam-diam. Misalnya, Anda tidak dapat menemukan kesalahan 404 dari server. Anda juga tidak dapat membatalkan atau memulai ulang permintaan. Anda dapat, bagaimanapun, waktu tunggu setelah menunggu waktu yang wajar. (Versi jQuery mendatang mungkin memiliki fitur batal untuk permintaan JSONP.)

Namun ada plug-in jsonp yang tersedia di GoogleCode yang menyediakan dukungan untuk penanganan kesalahan. Untuk memulai, cukup lakukan perubahan berikut pada kode Anda.

Anda dapat mengunduhnya, atau hanya menambahkan referensi skrip ke plugin.

<script type="text/javascript" 
     src="http://jquery-jsonp.googlecode.com/files/jquery.jsonp-1.0.4.min.js">
</script>

Kemudian ubah panggilan ajax Anda seperti yang ditunjukkan di bawah ini:

$(function(){
    //var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne"; // correct URL
    var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne_______"; // this should throw a 404  
    $.jsonp({
        url: jsonFeed,
        data: { "lang" : "en-us",
                "format" : "json",
                "tags" : "sunset"
        },
        dataType: "jsonp",
        callbackParameter: "jsoncallback",
        timeout: 5000,
        success: function(data, status){
            $.each(data.items, function(i,item){
                $("<img>").attr("src", (item.media.m).replace("_m.","_s."))
                          .attr("alt", item.title)
                          .appendTo("ul#flickr")
                          .wrap("<li><a href=\"" + item.link + "\"></a></li>");
                if (i == 9) return false;
            });
        },
        error: function(XHR, textStatus, errorThrown){
            alert("ERREUR: " + textStatus);
            alert("ERREUR: " + errorThrown);
        }
    });
});
Jose Basilio
sumber
Terima kasih untuk Anda menjawab José! Implementasi terbatas jsonp asli di jQuery adalah alasan saya memilih $ .ajax sebagai gantinya. Namun, tampaknya masalahnya bukan bahwa $ .getJSON adalah pembungkus sekitar $ .ajax tetapi dataType: jsonp. Apakah itu benar?
Matijs
Belum punya waktu sampai hari ini. Namun itu tidak berhasil. Saya mendapatkan kesalahan, tapi itu saja. Saya tidak tahu kesalahan apa karena errorThrown tidak ditentukan. Untuk saat ini saya akan tetap menggunakan $ .ajax dan kurangnya pesan kesalahan. Javascript adalah lapisan tambahan di atas halaman web untuk saya. Jika Flickr tidak aktif atau memberikan pesan kesalahan, kami harus menerimanya untuk saat ini :) Terima kasih atas saran Anda.
Matijs
Jadi pada dasarnya saran José menggunakan plugin jsonp seharusnya, jika dilakukan dengan benar, berfungsi. Untuk saat ini saya akan tetap menggunakan pembungkus json dasar untuk jQuery.
Matijs
Ini bekerja dengan sangat baik bagi saya setelah saya menabrak dinding dengan batasan bawaan dari penanganan kesalahan jsonp
Jeremy Frey
7
Pengembang lain diselamatkan oleh tip ini. Terima kasih!
chesterbr
12

Solusi jika Anda terjebak dengan jQuery 1.4:

var timeout = 10000;
var id = setTimeout( errorCallback, timeout );
$.ajax({
    dataType: 'jsonp',
    success: function() {
        clearTimeout(id);
        ...
    }
});
Rob De Almeida
sumber
2
hanya sebuah komentar, ini adalah solusi yang valid untuk orang-orang yang terjebak dengan 1.4, tetapi tetapkan variabel batas waktu Anda cukup tinggi, sehingga Anda tidak memiliki masalah dengan layanan web yang lambat :). jika Anda menyetelnya ke satu detik misalnya, Anda dapat melihat apa yang saya maksud, callback kesalahan dipicu, sementara setelah itu 1 detik panggilan Anda masih diambil dan memanggil fungsi sukses. jadi ingatlah untuk menetapkannya cukup tinggi
Sander
@Sander fungsi sukses bahkan dapat dipanggil jika Anda mendapat jawaban setelah 10 detik dengan waktu tunggu 5 detik. Memanggil .abort()jqXHR yang tertunda setelah periode waktu tunggu mungkin merupakan cara yang lebih baik.
Matijs
8

Ini mungkin merupakan batasan jQuery yang "diketahui"; Namun, tampaknya tidak didokumentasikan dengan baik. Saya menghabiskan sekitar 4 jam hari ini mencoba memahami mengapa waktu tunggu saya tidak berfungsi.

Saya beralih ke jquery.jsonp dan itu bekerja dengan sangat baik. Terima kasih.

Marc
sumber
2

Tampaknya diselesaikan pada jQuery 1.5. Saya sudah mencoba kode di atas dan mendapatkan panggilan balik.

Saya mengatakan "sepertinya" karena dokumentasi callback kesalahan untuk jQuery.ajax () masih memiliki catatan berikut:

Catatan: Penangan ini tidak dipanggil untuk skrip lintas domain dan permintaan JSONP.

Peter Tate
sumber
1

Plugin jquery-jsonp jQuery yang disebutkan dalam jawaban Jose Basilio sekarang dapat ditemukan di GitHub .

Sayangnya dokumentasinya agak sederhana, jadi saya telah memberikan contoh sederhana:

    $.jsonp({
        cache: false,
        url: "url",
        callbackParameter: "callback",
        data: { "key" : "value" },
        success: function (json, textStatus, xOptions) {
            // handle success - textStatus is "success"   
        },
        error: function (xOptions, textStatus) {
            // handle failure - textStatus is either "error" or "timeout"
        }
    });

Penting Sertakan callbackParameter dalam panggilan $ .jsonp () jika tidak, saya telah menemukan bahwa fungsi callback tidak pernah disuntikkan ke dalam string kueri panggilan layanan (saat ini pada versi 2.4.0 dari jquery-jsonp).

Saya tahu pertanyaan ini sudah lama, tetapi masalah penanganan kesalahan menggunakan JSONP masih belum 'terselesaikan'. Mudah-mudahan pembaruan ini akan membantu orang lain karena pertanyaan ini adalah peringkat teratas di Google untuk "penanganan kesalahan jquery jsonp".

OiDatsMyLeg
sumber
penemuan bagus, dan menakjubkan utas ini masih hidup setelah tiga tahun…
Matijs
1

Bagaimana dengan mendengarkan acara onerror skrip? Saya memiliki masalah ini dan menyelesaikannya dengan menambal metode jquery tempat tag skrip dibuat. Ini sedikit kode yang menarik:

// Attach handlers for all browsers
script.onload = script.onreadystatechange = function( _, isAbort ) {

    if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {

        cleanup();

        // Callback if not abort
        if ( !isAbort ) {
            callback( 200, "success" );
        }
    }
};

/* ajax patch : */
script.onerror = function(){
    cleanup();

    // Sends an inappropriate error, still better than nothing
    callback(404, "not found");
};

function cleanup(){
    // Handle memory leak in IE
    script.onload = script.onreadystatechange = null;

    // Remove the script
    if ( head && script.parentNode ) {
        head.removeChild( script );
    }

    // Dereference the script
    script = undefined;
}

Apa pendapat Anda tentang solusi ini? Apakah itu mungkin menyebabkan masalah kompatibilitas?

Hadrien Milano
sumber