Bagaimana cara mengembalikan respon dari panggilan asynchronous?

Aku punya fungsi foo yang membuat permintaan Ajax. Bagaimana saya bisa mengembalikan respon dari foo?

Aku mencoba kembali nilai dari keberhasilan callback serta memberikan respon terhadap variabel lokal dalam fungsi dan kembali yang satu itu, tapi tak satu pun dari cara-cara itu benar-benar kembali respon.

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });

    return result;
}

var result = foo(); // It always ends up being `undefined`.
Larutan

→ Untuk penjelasan yang lebih umum dari async perilaku yang berbeda dengan contoh-contoh, silakan lihat https://stackoverflow.com/q/23667086/218196

→ Jika anda sudah memahami masalah, lanjutkan ke solusi yang mungkin di bawah ini.

Masalah

Yang ** di Ajax singkatan [asynchronous**]2 . Itu berarti mengirimkan permintaan (atau lebih tepatnya menerima respon) diambil dari alur eksekusi normal. Dalam contoh anda, $.ajax kembali segera dan pernyataan berikutnya, return hasil;, dieksekusi sebelum fungsi anda lulus sebagai keberhasilan callback itu bahkan disebut. Berikut ini adalah analogi yang mudah-mudahan membuat perbedaan antara synchronous dan asynchronous aliran yang lebih jelas:

Sinkron

Bayangkan anda membuat panggilan telepon ke teman dan meminta dia untuk mencari sesuatu untuk anda. Meskipun mungkin memakan waktu beberapa saat, anda menunggu di telepon dan menatap ke ruang angkasa, sampai teman anda memberi anda jawaban yang anda butuhkan. Hal yang sama terjadi ketika anda membuat panggilan fungsi yang mengandung "normal" kode:

function findItem() {
    var item;
    while(item_not_found) {
        // search
    }
    return item;
}

var item = findItem();

// Do something with item
doSomethingElse();

Meskipun findItem mungkin memakan waktu lama untuk mengeksekusi, kode apapun yang datang setelah var item = findItem(); memiliki ke menunggu sampai fungsi ini mengembalikan hasil.

Asynchronous

Anda menelepon teman anda lagi untuk alasan yang sama. Tapi kali ini anda katakan padanya bahwa anda sedang terburu-buru dan dia harus menelepon anda kembali di ponsel anda. Anda menutup, meninggalkan rumah dan melakukan apa pun yang anda berencana untuk melakukan. Setelah teman anda memanggil anda kembali, anda berurusan dengan informasi yang dia berikan kepada anda. Yang's apa yang's terjadi ketika anda melakukan permintaan Ajax.

findItem(function(item) {
    // Do something with item
});
doSomethingElse();

Alih-alih menunggu respon, eksekusi berlanjut segera dan pernyataan setelah Ajax call dijalankan. Untuk mendapatkan respon akhirnya, anda menyediakan fungsi yang akan dipanggil setelah respon diterima, a panggil ulang (melihat sesuatu? panggil kembali ?). Setiap pernyataan yang datang setelah panggilan yang dieksekusi sebelum balik disebut.

Solusi(s)

Merangkul asynchronous alam JavaScript! Sedangkan tertentu operasi asynchronous memberikan sinkron rekan-rekan (jadi tidak "Ajax"), it's umumnya tidak dianjurkan untuk menggunakannya, terutama di browser konteks. Mengapa kau bertanya? JavaScript berjalan di UI thread browser dan panjang proses yang berjalan akan mengunci UI, sehingga tidak responsif. Selain itu, ada batas atas pada waktu eksekusi untuk JavaScript dan browser akan meminta pengguna apakah akan melanjutkan eksekusi atau tidak. Semua ini benar-benar buruk untuk pengguna. Pengguna tidak't dapat memberitahu apakah semuanya bekerja dengan baik atau tidak. Selain itu, efeknya akan lebih buruk untuk pengguna dengan koneksi yang lambat. Dalam berikut kita akan melihat tiga solusi yang berbeda yang semua bangunan di atas satu sama lain:

  • Janji dengan async/menanti (ES2017+, tersedia di browser lama jika anda menggunakan transpiler atau regenerator)
  • Callback (populer di node)
  • Dengan janji-janji maka() (ES2015+, tersedia di browser lama jika anda menggunakan salah satu dari banyak janji perpustakaan) Semua tiga tersedia dalam browser yang ada saat ini, dan node 7+.

    ES2017+: dengan janji-Janji async/menanti

    ECMAScript versi dirilis pada 2017 diperkenalkan sintaks-tingkat dukungan untuk fungsi asynchronous. Dengan bantuan async dan menunggu, anda dapat menulis asynchronous dalam "sinkron gaya". Kode ini masih asynchronous, tapi itu's mudah untuk membaca/memahami. async/menanti dibangun di atas janji-janji: sebuah async fungsi selalu kembali janji. menunggu "sesuatu" janji dan baik mengakibatkan nilai janji itu diselesaikan dengan atau melempar kesalahan jika janji itu ditolak. Penting: Anda hanya dapat menggunakan menginap di dalam async fungsi. Sekarang, tingkat atas menunggu isn't belum didukung, sehingga anda mungkin harus membuat async HIDUP (Segera Dipanggil Fungsi Ekspresi) untuk memulai sebuah async konteks. Anda dapat membaca lebih lanjut tentang async dan menunggu pada MDN. Berikut adalah contoh yang dibangun di atas keterlambatan atas:

// Using 'superagent' which will return a promise.
var superagent = require('superagent')

// This is isn't declared as `async` because it already returns a promise
function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}

async function getAllBooks() {
  try {
    // GET a list of book IDs of the current user
    var bookIDs = await superagent.get('/user/books');
    // wait for 3 seconds (just for the sake of this example)
    await delay();
    // GET information about each book
    return await superagent.get('/books/ids='+JSON.stringify(bookIDs));
  } catch(error) {
    // If any of the awaited promises was rejected, this catch block
    // would catch the rejection reason
    return null;
  }
}

// Start an IIFE to use `await` at the top level
(async function(){
  let books = await getAllBooks();
  console.log(books);
})();

Saat ini browser dan node versi dukungan async/menanti. Anda juga dapat mendukung lebih tua lingkungan dengan mengubah kode anda untuk ES5 dengan bantuan regenerator (atau alat-alat yang menggunakan regenerator, seperti Babel).

Mari fungsi menerima callback

Callback adalah hanya fungsi yang dilewatkan ke fungsi lain. Lainnya fungsi dapat memanggil fungsi berlalu kapan pun siap. Dalam konteks proses asynchronous, callback yang akan dipanggil setiap kali asynchronous proses ini dilakukan. Biasanya, hasilnya akan diteruskan ke callback. Dalam contoh dari pertanyaan, anda dapat membuat foo menerima panggilan balik dan menggunakannya sebagai keberhasilan callback. Jadi, ini

var result = foo();
// Code that depends on 'result'

menjadi

foo(function(result) {
    // Code that depends on 'result'
});

Di sini kita mendefinisikan fungsi "inline" tapi anda dapat melewati setiap fungsi referensi:

function myCallback(result) {
    // Code that depends on 'result'
}

foo(myCallback);

foo itu sendiri didefinisikan sebagai berikut:

function foo(callback) {
    $.ajax({
        // ...
        success: callback
    });
}

balik akan mengacu pada fungsi kita lulus untuk foo ketika kita panggil dan kita hanya lulus pada keberhasilan. I. e. setelah permintaan Ajax berhasil, $.ajax akan memanggil balik dan lulus menanggapi panggilan balik (yang dapat disebut dengan hasil, karena ini adalah bagaimana kita mendefinisikan callback). Anda juga dapat memproses respon sebelum diteruskan ke callback:

function foo(callback) {
    $.ajax({
        // ...
        success: function(response) {
            // For example, filter the response
            callback(filtered_response);
        }
    });
}

It's mudah untuk menulis kode menggunakan callback dari itu mungkin tampak. Setelah semua, JavaScript dalam browser ini sangat-event (peristiwa DOM). Menerima Ajax respon tidak lain adalah sebuah event. Kesulitan bisa timbul ketika anda harus bekerja dengan kode pihak ketiga, tetapi sebagian besar masalah dapat diselesaikan dengan hanya berpikir melalui alur aplikasi.

ES2015+: janji-Janji dengan maka()

The Janji API adalah sebuah fitur baru dari ECMAScript 6 (ES2015), tetapi memiliki baik dukungan browser sudah. Ada juga banyak perpustakaan yang menerapkan standar Menjanjikan API dan memberikan tambahan metode untuk kemudahan penggunaan dan komposisi asynchronous fungsi (misalnya bluebird). Janji adalah wadah untuk masa depan nilai-nilai. Ketika janji menerima nilai (itu menyelesaikan) atau ketika itu dibatalkan (ditolak), itu akan memberitahu semua "pendengar" yang ingin mengakses nilai ini. Keuntungan atas polos callback adalah bahwa mereka memungkinkan anda untuk memisahkan kode anda dan mereka lebih mudah untuk menulis. Berikut ini adalah contoh sederhana menggunakan berjanji:

function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}

delay()
  .then(function(v) { // `delay` returns a promise
    console.log(v); // Log the value once it is resolved
  })
  .catch(function(v) {
    // Or do something else if it is rejected 
    // (it would not happen in this example, since `reject` is not called).
  });

Diterapkan untuk kami Ajax call kita bisa menggunakan janji-janji seperti ini:

function ajax(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open('GET', url);
    xhr.send();
  });
}

ajax("/echo/json")
  .then(function(result) {
    // Code depending on result
  })
  .catch(function() {
    // An error occurred
  });

Menggambarkan semua keuntungan yang menjanjikan tawarkan adalah di luar lingkup dari jawaban ini, tapi jika anda menulis kode baru, anda harus secara serius mempertimbangkan mereka. Mereka memberikan abstraksi dan pemisahan dari kode anda. Informasi lebih lanjut tentang janji-janji: HTML5 batu - JavaScript janji-Janji

Catatan: jQuery's tangguhan benda

Tangguhan benda ini adalah jQuery's custom pelaksanaan janji-janji (sebelum Janji API adalah standar). Mereka berperilaku hampir seperti janji-janji tapi mengekspos sedikit berbeda API. Setiap metode Ajax jQuery sudah kembali "tangguhan objek" (sebenarnya janji tangguhan objek) yang dapat anda baru saja kembali dari fungsi:

function ajax() {
    return $.ajax(...);
}

ajax().done(function(result) {
    // Code depending on result
}).fail(function() {
    // An error occurred
});

Catatan: Janji gotchas

Perlu diingat bahwa janji-janji dan tangguhan benda-benda yang hanya wadah untuk nilai masa depan, mereka bukan nilai itu sendiri. Untuk contoh, misalkan anda memiliki yang berikut ini:

function checkPassword() {
    return $.ajax({
        url: '/password',
        data: {
            username: $('#username').val(),
            password: $('#password').val()
        },
        type: 'POST',
        dataType: 'json'
    });
}

if (checkPassword()) {
    // Tell the user they're logged in
}

Ini salah memahami kode di atas asynchrony masalah. Secara khusus, $.ajax() doesn't membekukan kode sementara itu cek '/password' halaman pada server anda - ia akan mengirimkan permintaan ke server dan sementara menunggu, segera kembali jQuery Ajax Tangguhan objek, bukan respon dari server. Yang berarti jika pernyataan akan selalu mendapatkan ini Ditangguhkan objek, memperlakukannya sebagai benar, dan melanjutkan seolah-olah user tersebut login. Tidak baik. Tapi perbaiki mudah:

checkPassword()
.done(function(r) {
    if (r) {
        // Tell the user they're logged in
    } else {
        // Tell the user their password was bad
    }
})
.fail(function(x) {
    // Tell the user something bad happened
});

Tidak dianjurkan: Sinkron "Ajax" panggilan

Seperti yang saya sebutkan, beberapa(!) operasi asynchronous telah sinkron rekan-rekan. Saya don't advokat yang mereka gunakan, tapi untuk kelengkapan' demi, di sini adalah bagaimana anda akan melakukan panggilan sinkron:

Tanpa jQuery

Jika anda langsung menggunakan XMLHTTPRequest objek, lulus palsu sebagai argumen ketiga untuk .open.

jQuery

Jika anda menggunakan jQuery, anda dapat mengatur async pilihan untuk palsu. Perhatikan bahwa opsi ini adalah tidak berlaku lagi sejak jQuery 1.8. Anda kemudian dapat baik yang masih menggunakan sukses callback atau mengakses responseText milik jqXHR objek:

function foo() {
    var jqXHR = $.ajax({
        //...
        async: false
    });
    return jqXHR.responseText;
}

Jika anda menggunakan jQuery Ajax metode, seperti $.dapatkan, $.getJSON, dll., anda harus mengubahnya ke $.ajax (karena anda hanya dapat melewatkan parameter konfigurasi untuk $.ajax). Kepala-up! Hal ini tidak mungkin untuk membuat sebuah sinkron JSONP permintaan. JSONP dengan sifatnya yang selalu asynchronous (satu lagi alasan untuk tidak bahkan mempertimbangkan opsi ini).

Komentar (35)

Jika anda're not menggunakan jQuery dalam kode anda, jawaban ini adalah untuk anda

Kode anda harus menjadi sesuatu di sepanjang baris ini:

function foo() {
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
    return httpRequest.responseText;
}

var result = foo(); // always ends up being 'undefined'

Felix Kling melakukan pekerjaan baik menulis jawaban untuk orang-orang yang menggunakan jQuery untuk AJAX, I've memutuskan untuk memberikan alternatif bagi orang-orang yang tidak't.

(Catatan, bagi mereka yang menggunakan baru mengambil API, Sudut atau janji-janji yang saya've menambahkan jawaban lain di bawah ini)


Apa yang anda're menghadapi

Ini adalah ringkasan singkat dari "Penjelasan masalah" dari yang lain menjawab, jika anda'kembali tidak pasti setelah baca ini, baca itu.

Yang ** di AJAX merupakan singkatan dari asynchronous**. Itu berarti mengirimkan permintaan (atau lebih tepatnya menerima respon) diambil dari alur eksekusi normal. Dalam contoh anda, .kirim kembali segera dan pernyataan berikutnya, return hasil;, dieksekusi sebelum fungsi anda lulus sebagai keberhasilan callback itu bahkan disebut.

Ini berarti ketika anda're kembali, pendengar anda'telah ditentukan tidak melaksanakan lagi, yang berarti nilai anda're kembali belum didefinisikan.

Berikut ini adalah analogi sederhana

function getFive(){ 
    var a;
    setTimeout(function(){
         a=5;
    },10);
    return a;
}

[(Biola)][2]

Nilai dari a kembali undefined sejak a=5 bagian tidak dieksekusi lagi. AJAX bertindak seperti ini, anda're kembali ke nilai sebelum server punya kesempatan untuk memberitahu browser anda apa itu nilai lebih.

Salah satu solusi yang mungkin untuk masalah ini adalah untuk kode re-actively , mengatakan program anda apa yang harus dilakukan ketika perhitungan selesai.

function onComplete(a){ // When the code completes, do this
    alert(a);
}

function getFive(whenDone){ 
    var a;
    setTimeout(function(){
         a=5;
         whenDone(a);
    },10);
}

Ini disebut CPS. Pada dasarnya, kita're lewat getFive tindakan untuk melakukan ketika itu selesai, kami're memberitahu kode kita bagaimana harus bereaksi ketika acara selesai (seperti AJAX call, atau dalam hal ini timeout).

Penggunaan akan sama:

getFive(onComplete);

Yang harus waspada "5" untuk layar. [(Biola)][4].

Solusi yang mungkin

Pada dasarnya ada dua cara bagaimana untuk memecahkan masalah ini:

  1. Membuat AJAX call sinkron (memungkinkan menyebutnya SJAX).
  2. Merestrukturisasi kode anda untuk bekerja dengan baik dengan callback.

1. Sinkron AJAX - Don't melakukan itu!!

Adapun sinkron AJAX, don't melakukan itu! Felix's jawaban menimbulkan beberapa argumen menarik tentang mengapa hal itu's merupakan ide yang buruk. Untuk jumlah itu, it'll membekukan pengguna's browser hingga server kembali respon dan membuat sangat buruk untuk pengguna. Berikut ini adalah ringkasan singkat yang diambil dari MDN mengapa:

XMLHttpRequest mendukung sinkron dan asinkron komunikasi. Secara umum, bagaimanapun, permintaan asynchronous harus diutamakan untuk sinkron permintaan untuk alasan kinerja.

singkatnya, sinkron permintaan memblokir eksekusi kode... ...hal ini dapat menyebabkan masalah yang serius...

Jika anda have untuk melakukan ini, anda dapat melewati bendera: di Sini adalah bagaimana:

var request = new XMLHttpRequest();
request.open('GET', 'yourURL', false);  // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {// That's HTTP for 'ok'
  console.log(request.responseText);
}

2. Merestrukturisasi kode

Mari fungsi anda menerima panggilan balik. Dalam contoh kode foo dapat dibuat untuk menerima panggilan balik. Kami'akan menceritakan kepada kami kode cara react ketika foo selesai.

Jadi:

var result = foo();
// code that depends on `result` goes here

Menjadi:

foo(function(result) {
    // code that depends on `result`
});

Di sini kami melewati fungsi anonim, tapi kita bisa dengan mudah melewati sebuah referensi yang ada fungsi, sehingga terlihat seperti:

function myHandler(result) {
    // code that depends on `result`
}
foo(myHandler);

Untuk rincian lebih lanjut tentang cara semacam ini callback desain selesai, periksa Felix's jawaban.

Sekarang, let's menentukan foo sendiri untuk bertindak sesuai

function foo(callback) {
    var httpRequest = new XMLHttpRequest();
    httpRequest.onload = function(){ // when the request is loaded
       callback(httpRequest.responseText);// we're calling our method
    };
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
}

[(biola)][6]

Sekarang kita telah membuat kami foo fungsi menerima tindakan untuk menjalankan ketika AJAX selesai dengan sukses, kita dapat memperpanjang ini lebih lanjut dengan memeriksa jika respon status tidak 200 dan bertindak sesuai dengan itu (membuat gagal handler dan semacamnya). Dapat secara efektif memecahkan masalah kami.

Jika anda're masih memiliki waktu yang sulit memahami ini baca AJAX panduan memulai di MDN.

Komentar (4)

XMLHttpRequest 2 (pertama-tama membaca jawaban dari Benjamin Gruenbaum & Felix Kling) Jika anda don't menggunakan jQuery dan ingin pendek yang bagus XMLHttpRequest 2 yang bekerja pada browser modern dan juga di mobile browser, saya sarankan untuk menggunakan cara ini:

function ajax(a, b, c){ // URL, callback, just a placeholder
  c = new XMLHttpRequest;
  c.open('GET', a);
  c.onload = b;
  c.send()
}

Seperti yang anda lihat:

  1. It's lebih pendek dari semua fungsi-fungsi lain yang Terdaftar.
  2. Callback diatur langsung (jadi tidak ada tambahan yang tidak perlu penutupan).
  3. Menggunakan baru onload (sehingga anda don't harus memeriksa untuk readystate && status)
  4. Ada beberapa situasi lain yang saya don't ingat bahwa membuat XMLHttpRequest 1 menjengkelkan. Ada dua cara untuk mendapatkan respon ini Ajax call (tiga menggunakan XMLHttpRequest var nama): Yang paling sederhana:
this.response

Atau jika untuk beberapa alasan anda bind() callback untuk kelas:

e.target.response

Contoh:

function callback(e){
  console.log(this.response);
}
ajax('URL', callback);

Atau (di atas satu lebih baik fungsi anonim yang selalu menjadi masalah):

ajax('URL', function(e){console.log(this.response)});

Ada yang lebih mudah. Sekarang beberapa orang mungkin akan mengatakan bahwa itu's baik untuk menggunakan onreadystatechange atau bahkan XMLHttpRequest nama variabel. Yang's salah. Check out XMLHttpRequest fitur-fitur canggih Hal itu didukung semua *browser modern. Dan saya bisa mengkonfirmasi karena saya'm menggunakan pendekatan ini karena XMLHttpRequest 2 ada. Aku tidak pernah punya jenis masalah pada semua browser yang saya gunakan. onreadystatechange ini hanya berguna jika anda ingin mendapatkan header pada negara 2. Menggunakan XMLHttpRequest nama variabel lain adalah kesalahan besar yang anda butuhkan untuk menjalankan callback dalam onload/oreadystatechange penutupan yang lain anda kehilangan itu.

Sekarang jika anda ingin sesuatu yang lebih kompleks menggunakan pos dan FormData anda dapat dengan mudah memperluas fungsi ini:

function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.send(d||null)
}

Sekali lagi ... itu's yang sangat singkat fungsi, tetapi tidak mendapatkan & posting. Contoh penggunaan:

x(url, callback); // By default it's get so no need to set
x(url, callback, 'post', {'key': 'val'}); // No need to set post data

Atau lulus penuh elemen form (document.getElementsByTagName(&#39;bentuk&#39;)[0]):

var fd = new FormData(form);
x(url, callback, 'post', fd);

Atau mengatur beberapa nilai-nilai adat:

var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);

Seperti yang anda lihat saya didn't melaksanakan sinkronisasi... itu's hal yang buruk. Setelah mengatakan bahwa ... mengapa don't melakukannya dengan cara yang mudah?

Seperti yang disebutkan dalam komentar penggunaan kesalahan && sinkron tidak benar-benar istirahat titik jawabannya. Yang bagus cara singkat untuk menggunakan Ajax dalam cara yang tepat? Error handler

function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val}, placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.onerror = error;
  c.send(d||null)
}

function error(e){
  console.log('--Error--', this.type);
  console.log('this: ', this);
  console.log('Event: ', e)
}
function displayAjax(e){
  console.log(e, this);
}
x('WRONGURL', displayAjax);

Dalam script di atas, anda memiliki error handler yang statis didefinisikan sehingga tidak membahayakan fungsi. Error handler dapat digunakan untuk fungsi-fungsi lain juga. Tapi untuk benar-benar keluar kesalahan hanya cara adalah dengan menulis URL yang salah dalam hal ini setiap browser melempar kesalahan. Penangan kesalahan yang mungkin berguna jika anda mengatur header kustom, mengatur responseType untuk blob array buffer atau apapun... Bahkan jika anda lulus 'POSTAPAPAP' sebagai metode itu tidak't melempar kesalahan. Bahkan jika anda lulus 'fdggdgilfdghfldj' sebagai formdata itu tidak't melempar kesalahan. Dalam kasus pertama kesalahan dalam displayAjax() bawah ini.statusText sebagai Metode tidak Diizinkan. Dalam kedua kasus, itu hanya bekerja. Anda harus memeriksa pada sisi server jika anda melewati tiang kanan data. cross-domain tidak diperbolehkan melempar kesalahan secara otomatis. Dalam respon kesalahan, tidak ada kesalahan kode. Hanya ada ini.ketik yang diatur untuk kesalahan. Mengapa menambahkan error handler jika anda benar-benar tidak memiliki kontrol atas kesalahan? Sebagian besar kesalahan kembali dalam hal ini fungsi callback displayAjax(). Jadi: Tidak perlu untuk pengecekan kesalahan jika anda're dapat copy dan paste URL dengan benar. ;) PS: Sebagai tes pertama saya menulis x('x', displayAjax)..., dan itu benar-benar mendapat respon...??? Jadi aku memeriksa folder mana HTML-terletak, dan ada sebuah file yang bernama 'x.xml'. Jadi bahkan jika anda lupa ekstensi file anda XMLHttpRequest 2 AKAN menemukannya. Saya LOL'd

Baca file sinkron Don't melakukan itu. Jika anda ingin memblokir browser untuk sementara beban besar yang bagus .txt file sinkron.

function omg(a, c){ // URL
  c = new XMLHttpRequest;
  c.open('GET', a, true);
  c.send();
  return c; // Or c.response
}

Sekarang anda dapat melakukan

 var res = omg('thisIsGonnaBlockThePage.txt');

Tidak ada cara lain untuk melakukan ini di non-asynchronous cara. (Ya, dengan setTimeout loop... tapi serius?) Titik lain adalah... jika anda bekerja dengan Api atau hanya daftar anda sendiri's file atau apapun yang anda selalu menggunakan fungsi yang berbeda untuk setiap permintaan... Hanya jika anda memiliki sebuah halaman di mana anda memuat selalu sama XML/JSON atau apa pun yang anda butuhkan hanya satu fungsi. Dalam kasus itu, memodifikasi sedikit Ajax fungsi dan mengganti b dengan fungsi khusus.

Fungsi di atas adalah untuk penggunaan dasar. Jika anda ingin MEMPERPANJANG fungsi... Ya, anda bisa. I'm menggunakan banyak Api dan salah satu fungsi yang saya mengintegrasikan ke setiap halaman HTML adalah pertama fungsi Ajax di jawaban ini, dengan hanya MENDAPATKAN... Tetapi anda dapat melakukan banyak hal dengan XMLHttpRequest 2: Saya membuat sebuah download manager (menggunakan berkisar di kedua sisi dengan resume, filereader, filesystem), berbagai resizers gambar konverter menggunakan kanvas, mengisi web SQL database dengan base64images dan banyak lagi... Tapi dalam kasus ini anda harus membuat sebuah fungsi hanya untuk tujuan itu... kadang-kadang anda perlu gumpalan, array buffer, anda dapat mengatur header, mengganti tipe mime dan ada lebih banyak... Tapi pertanyaannya di sini adalah bagaimana untuk kembali ke Ajax respon... (saya menambahkan cara yang mudah.)

Komentar (7)

Jika anda're menggunakan janji-janji, ini jawabannya untuk anda.

Ini berarti AngularJS, jQuery (ditangguhkan), XHR asli's penggantian (fetch), EmberJS, BackboneJS's menyimpan atau setiap node perpustakaan yang kembali janji-janji. Kode anda harus menjadi sesuatu di sepanjang baris ini:

function foo() {
    var data;
    // or $.get(...).then, or request(...).then, or query(...).then
    fetch("/echo/json").then(function(response){
        data = response.json();
    });
    return data;
}

var result = foo(); // result is always undefined no matter what.

Felix Kling melakukan pekerjaan baik menulis jawaban untuk orang-orang yang menggunakan jQuery dengan callback untuk AJAX. Aku punya jawaban untuk XHR asli. Jawaban ini adalah untuk penggunaan generik dari janji-janji baik di frontend atau backend.

Masalah inti

JavaScript concurrency model di browser dan server dengan NodeJS/io.js adalah asynchronous dan reactive. Setiap kali anda memanggil sebuah metode yang mengembalikan sebuah janji, kemudian penangan always dieksekusi asynchronously - yang, setelah kode di bawah mereka yang tidak dalam .kemudian handler. Ini berarti ketika anda're kembali data kemudian handler anda've didefinisikan tidak mengeksekusi belum. Hal ini pada gilirannya berarti bahwa nilai anda're kembali belum diatur ke nilai yang benar pada waktunya. Berikut ini adalah analogi sederhana untuk masalah ini:

    function getFive(){
        var data;
        setTimeout(function(){ // set a timer for one second in the future
           data = 5; // after a second, do this
        }, 1000);
        return data;
    }
    document.body.innerHTML = getFive(); // `undefined` here and not 5

Nilai dari data adalah undefined sejak data = 5 bagian tidak dieksekusi lagi. Kemungkinan akan mengeksekusi di kedua tetapi pada saat itu adalah tidak relevan dengan nilai yang dikembalikan. Sejak operasi belum terjadi (AJAX, server call, IO, timer) anda're kembali ke nilai sebelum permohonan punya kesempatan untuk memberitahu anda kode apa itu nilai lebih. Salah satu solusi yang mungkin untuk masalah ini adalah untuk kode re-actively , mengatakan program anda apa yang harus dilakukan ketika perhitungan selesai. Janji-janji aktif mengaktifkan ini dengan menjadi temporal (waktu-sensitif) di alam.

Rekap cepat pada janji-janji

Janji adalah value atas time. Janji negara, mereka mulai sebagai pending dengan tidak ada nilai dan dapat menetap untuk:

  • dipenuhi artinya perhitungan selesai dengan sukses.
  • menolak artinya perhitungan gagal. Janji hanya dapat mengubah serikat once setelah itu akan selalu tinggal di negara yang sama selamanya. Anda dapat melampirkan kemudian penangan untuk janji-janji untuk mengekstrak nilai mereka dan menangani kesalahan. kemudian penangan memungkinkan chaining dari panggilan. Janji-janji yang dibuat oleh menggunakan Api yang kembali them. Misalnya, lebih modern AJAX pengganti ambil atau jQuery's $.dapatkan kembali janji-janji. Ketika kita menyebut .kemudian pada janji dan return sesuatu dari hal itu - kita mendapatkan janji untuk the diproses value. Jika kita kembali lagi janji kita'll mendapatkan hal-hal yang menakjubkan, tapi let's menahan kuda-kuda kami.

    Dengan janji-janji

    Let's melihat bagaimana kita dapat memecahkan masalah di atas dengan janji-janji. Pertama, let's menunjukkan pemahaman kita tentang janji serikat dari atas dengan menggunakan Janji constructor untuk menciptakan fungsi delay:

function delay(ms){ // takes amount of milliseconds
    // returns a new promise
    return new Promise(function(resolve, reject){
        setTimeout(function(){ // when the time is up
            resolve(); // change the promise to the fulfilled state
        }, ms);
    });
}

Sekarang, setelah kita dikonversi setTimeout untuk menggunakan janji-janji, kita dapat menggunakan kemudian untuk membuat hitungan:

function delay(ms){ // takes amount of milliseconds
  // returns a new promise
  return new Promise(function(resolve, reject){
    setTimeout(function(){ // when the time is up
      resolve(); // change the promise to the fulfilled state
    }, ms);
  });
}

function getFive(){
  // we're RETURNING the promise, remember, a promise is a wrapper over our value
  return delay(100).then(function(){ // when the promise is ready
      return 5; // return the value 5, promises are all about return values
  })
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){ 
   document.body.innerHTML = five;
});

Pada dasarnya, bukannya kembali value yang kita dapat't lakukan karena concurrency model - kita're kembali wrapper untuk nilai yang kita dapat unwrap dengan kemudian. It's seperti sebuah kotak yang dapat anda buka dengan kemudian.

Menerapkan ini

Ini adalah singkatan yang sama untuk anda asli API panggilan, anda dapat:

function foo() {
    // RETURN the promise
    return fetch("/echo/json").then(function(response){
        return response.json(); // process it inside the `then`
    });
}

foo().then(function(response){
    // access the value inside the `then`
})

Jadi ini bekerja sama dengan baik. Kami've belajar kita bisa't kembali nilai-nilai dari yang sudah panggilan asynchronous tapi kita bisa menggunakan janji-janji dan rantai mereka untuk melakukan pengolahan. Sekarang kita tahu bagaimana untuk mengembalikan respon dari panggilan asynchronous.

ES2015 (ES6)

ES6 memperkenalkan generator yang adalah fungsi yang dapat kembali di tengah dan kemudian melanjutkan point mereka di. Ini biasanya berguna untuk urutan, misalnya:

function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
    yield 1;
    yield 2;
    while(true) yield 3;
}

Adalah fungsi yang mengembalikan sebuah iterator urutan 1,2,3,3,3,3,.... yang dapat diiterasi. Sementara ini menarik sendiri dan membuka ruang untuk banyak kemungkinan ada salah satu kasus yang menarik. Jika urutan kita're memproduksi adalah suatu urutan dari tindakan-tindakan dan bukan angka-angka - kita bisa pause berfungsi setiap kali tindakan menyerah dan menunggu untuk itu sebelum kita melanjutkan fungsi. Jadi bukan urutan dari angka-angka, kita perlu urutan future nilai - nilai itu adalah: janji-janji. Ini agak rumit tapi sangat kuat trik yang memungkinkan kita menulis kode asinkron di sinkron dengan cara. Ada beberapa "pelari" yang melakukan ini untuk anda, menulis satu lebih pendek beberapa baris kode, tetapi adalah di luar lingkup dari jawaban ini. I'akan menggunakan Bluebird's Janji.coroutine di sini, tapi ada pembungkus lain seperti co atau Q. async.

var foo = coroutine(function*(){
    var data = yield fetch("/echo/json"); // notice the yield
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
});

Metode ini kembali janji itu sendiri, yang dapat kita konsumsi dari yang lain coroutines. Misalnya:

var main = coroutine(function*(){
   var bar = yield foo(); // wait our earlier coroutine, it returns a promise
   // server call done here, code below executes when done
   var baz = yield fetch("/api/users/"+bar.userid); // depends on foo's result
   console.log(baz); // runs after both requests done
});
main();

ES2016 (ES7)

Di ES7, hal ini lebih standar, ada beberapa proposal yang sekarang, tapi pada semua dari mereka, anda bisa menunggu janji. Ini adalah hanya "gula" (lebih bagus sintaks) untuk ES6 proposal di atas dengan menambahkan async dan menunggu kata kunci. Membuat contoh di atas:

async function foo(){
    var data = await fetch("/echo/json"); // notice the await
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
}

Itu masih kembali janji sama saja :)

Komentar (1)

Anda menggunakan Ajax dengan benar. Idenya adalah untuk tidak memilikinya kembali apa-apa, tapi malah menyerahkan data ke sesuatu yang disebut fungsi callback, yang menangani data.

Yaitu:

function handleData( responseData ) {

    // Do what you want with the data
    console.log(responseData);
}

$.ajax({
    url: "hi.php",
    ...
    success: function ( data, status, XHR ) {
        handleData(data);
    }
});

Kembali apa-apa di kirim handler tidak akan melakukan apa-apa. Sebagai gantinya, anda harus tangan baik dari data, atau melakukan apa yang anda inginkan dengan itu secara langsung dalam keberhasilan fungsi.

Komentar (4)

Solusi yang paling sederhana adalah membuat sebuah fungsi JavaScript dan panggilan untuk Ajax sukses callback.

function callServerAsync(){
    $.ajax({
        url: '...',
        success: function(response) {

            successCallback(response);
        }
    });
}

function successCallback(responseObj){
    // Do something like read the response and show data
    alert(JSON.stringify(responseObj)); // Only applicable to JSON response
}

function foo(callback) {

    $.ajax({
        url: '...',
        success: function(response) {
           return callback(null, response);
        }
    });
}

var result = foo(function(err, result){
          if (!err)
           console.log(result);    
}); 
Komentar (4)

Saya akan menjawab dengan mengerikan yang tampak, digambar tangan komik. Gambar kedua adalah alasan mengapa hasil adalah undefined di kode anda contoh.

Komentar (4)

Angular1

Untuk orang-orang yang menggunakan AngularJS, dapat menangani situasi ini dengan menggunakan janji-Janji.

Di sini ia berkata,

janji-Janji yang dapat digunakan untuk unnest asynchronous fungsi dan memungkinkan seseorang untuk rantai beberapa fungsi bersama-sama.

Anda dapat menemukan baik penjelasan di sini juga.

Contoh yang ditemukan dalam docs disebutkan di bawah ini.

  promiseB = promiseA.then(
    function onSuccess(result) {
      return result + 1;
    }
    ,function onError(err) {
      //Handle error
    }
  );

 // promiseB will be resolved immediately after promiseA is resolved 
 // and its value will be the result of promiseA incremented by 1.

Angular2 dan Kemudian

Di Angular2 dengan melihat contoh berikut ini, tapi recommended untuk menggunakan Diamati dengan Angular2.

 search(term: string) {
     return this.http
  .get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
  .map((response) => response.json())
  .toPromise();

}

Anda dapat mengkonsumsi dengan cara ini,

search() {
    this.searchService.search(this.searchField.value)
      .then((result) => {
    this.result = result.artists.items;
  })
  .catch((error) => console.error(error));
}

Lihat original posting di sini. Tapi Ketangkasan tidak mendukung asli es6 janji-Janji, jika anda ingin menggunakannya, anda mungkin perlu plugin untuk itu.

Selain itu di sini adalah janji-janji spec mendefinisikan sini.

Komentar (3)

Sebagian besar jawaban di sini memberikan saran-saran yang berguna untuk ketika anda memiliki satu operasi asinkron, tapi kadang-kadang, ini muncul ketika anda perlu untuk melakukan operasi asinkron untuk setiap masuk dalam array atau list-seperti struktur. Godaan untuk melakukan hal ini:

// WRONG
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log(results); // E.g., using them, returning them, etc.

Contoh:

// WRONG
var theArray = [1, 2, 3];
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log("Results:", results); // E.g., using them, returning them, etc.

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

Alasan yang doesn't bekerja adalah bahwa callback dari doSomethingAsync belum't berjalan namun dengan waktu anda're mencoba untuk menggunakan hasil. Jadi, jika anda memiliki sebuah array (atau daftar dari beberapa jenis) dan ingin melakukan operasi async untuk setiap entri, anda memiliki dua pilihan: Melakukan operasi secara paralel (tumpang tindih), atau seri (satu demi satu dalam urutan).

Paralel

Anda dapat memulai semua dari mereka dan menjaga melacak dari berapa banyak callback anda're mengharapkan, dan kemudian menggunakan hasil ketika anda've mendapat banyak callback:

var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

Contoh:

var theArray = [1, 2, 3];
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(Kita bisa melakukan jauh dengan mengharapkan dan hanya menggunakan hasil.panjang === ukuranarray.panjang, tapi yang membuat kita terbuka untuk kemungkinan bahwa ukuranarray berubah sementara panggilan yang luar biasa...) Perhatikan bagaimana kita menggunakan index dari forEach untuk menyimpan hasilnya di hasil di posisi yang sama sebagai entri ini berkaitan dengan, bahkan jika hasil tiba out of order (sejak async panggilan don't harus lengkap dalam urutan di mana mereka mulai). Tapi bagaimana jika anda perlu untuk kembali hasil-hasil dari sebuah fungsi? Sebagai jawaban lain telah menunjukkan, anda dapat't; anda harus memiliki fungsi menerima dan menelepon balik (atau kembali Janji). Berikut ini's callback versi:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

Contoh:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

Atau di sini's versi kembali Janji sebagai gantinya:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Tentu saja, jika doSomethingAsync melewati kami kesalahan-kesalahan, kita'd gunakan menolak untuk menolak janji ketika kita punya kesalahan.) Contoh:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(Atau secara bergantian, anda bisa membuat wrapper untuk doSomethingAsync yang kembali berjanji, dan kemudian lakukan hal di bawah ini...) Jika doSomethingAsync memberi anda Janji, anda dapat menggunakan Janji.semua:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry);
    }));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Jika anda tahu bahwa doSomethingAsync akan mengabaikan kedua dan ketiga argumen, anda hanya dapat melewati langsung ke peta (peta panggilan balik dengan tiga argumen, tapi kebanyakan orang hanya menggunakan sebagian besar waktu):

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Contoh:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

Perhatikan bahwa Janji.semua menyelesaikan janjinya dengan array hasil dari semua janji-janji yang anda berikan ketika mereka semua diselesaikan, atau menolak janjinya ketika pertama dari janji-janji yang kau berikan itu menolak.

Seri

Misalkan anda don't ingin operasi yang harus di paralel? Jika anda ingin menjalankan mereka satu demi satu, anda harus menunggu untuk setiap operasi untuk menyelesaikan sebelum anda memulai berikutnya. Berikut ini's contoh dari fungsi yang melakukan itu dan panggilan panggilan balik dengan hasil:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

(Karena kita're melakukan pekerjaan dalam seri ini, kita hanya dapat menggunakan hasil.push(hasil) karena kita tahu kita tidak't mendapatkan hasil yang keluar dari urutan. Di atas kita bisa menggunakan hasil[index] = hasil;, namun dalam beberapa contoh berikut kita don't memiliki indeks untuk digunakan.) Contoh:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(Atau, sekali lagi, membangun sebuah wrapper untuk doSomethingAsync yang memberikan janji dan melakukan di bawah ini...) Jika doSomethingAsync memberikan anda sebuah Janji, jika anda dapat menggunakan ES2017+ sintaks (mungkin dengan transpiler seperti Babel), anda dapat menggunakan async fungsi dengan bagi-dari dan menunggu:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

Contoh:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

Jika anda dapat't menggunakan ES2017+ sintaks (belum), anda dapat menggunakan variasi pada "Janji mengurangi" pola (ini lebih kompleks dari yang biasa Berjanji mengurangi karena kami're tidak melewati hasil dari yang satu ke yang berikutnya, tapi bukannya mengumpulkan hasil mereka dalam array):

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Contoh:

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

...yang kurang rumit dengan ES2015+ panah fungsi:

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

Contoh:

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}
Komentar (3)

Lihat contoh ini:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope,$http) {

    var getJoke = function(){
        return $http.get('http://api.icndb.com/jokes/random').then(function(res){
            return res.data.value;  
        });
    }

    getJoke().then(function(res) {
        console.log(res.joke);
    });
});

Seperti yang anda lihat getJoke adalah kembali diselesaikan janji (diselesaikan ketika kembali res.data.nilai). Jadi anda menunggu sampai $http.dapatkan permintaan selesai dan kemudian console.log(res.joke) dieksekusi (seperti normal asynchronous flow).

Ini adalah plnkr:

http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/

ES6 cara (async - menanti)

(function(){
  async function getJoke(){
    let response = await fetch('http://api.icndb.com/jokes/random');
    let data = await response.json();
    return data.value;
  }

  getJoke().then((joke) => {
    console.log(joke);
  });
})();
Komentar (0)

Ini adalah salah satu tempat yang dua cara mengikat data atau toko konsep yang's digunakan dalam kerangka kerja JavaScript akan bekerja besar untuk anda...

Jadi jika anda menggunakan Sudut, Bereaksi atau framework lain yang melakukan dua cara mengikat data atau toko konsep masalah ini hanya tetap untuk anda, sehingga mudah di word, anda hasilnya adalah undefined di tahap pertama, maka anda sudah mendapat hasil = tidak terdefinisi sebelum anda menerima data, kemudian segera setelah anda mendapatkan hasil, itu akan diperbarui dan bisa ditugaskan untuk nilai baru yang respon dari Ajax call...

Tapi bagaimana anda bisa melakukan itu di murni javascript atau jQuery misalnya anda bertanya pertanyaan ini?

Anda dapat menggunakan panggil ulang, janji baru diamati untuk menangani hal ini untuk anda, misalnya dalam janji-janji kita memiliki beberapa fungsi seperti sukses() atau maka() yang akan dieksekusi ketika data anda siap untuk anda, yang sama dengan callback atau berlangganan fungsi diamati.

Misalnya dalam kasus anda yang anda gunakan jQuery, anda dapat melakukan sesuatu seperti ini:

$(document).ready(function(){
    function foo() {
        $.ajax({url: "api/data", success: function(data){
            fooDone(data); //after we have data, we pass it to fooDone
        }});
    };

    function fooDone(data) {
        console.log(data); //fooDone has the data and console.log it
    };

    foo(); //call happens here
});

Untuk informasi lebih lanjut belajar tentang janji dan diamati yang cara-cara baru untuk melakukan hal ini async barang.

Komentar (3)

Pendekatan lain untuk mengembalikan sebuah nilai dari sebuah fungsi asynchronous, adalah untuk lulus dalam suatu objek yang akan menyimpan hasil dari asynchronous fungsi.

Berikut adalah contoh yang sama:

var async = require("async");

// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.push(function(_callback){
    // some asynchronous operation
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;
            _callback();
        }
    });
});

async.parallel(asyncTasks, function(){
    // result is available after performing asynchronous operation
    console.log(result)
    console.log('Done');
});

Saya menggunakan hasil objek untuk menyimpan nilai selama operasi asynchronous. Hal ini memungkinkan hasilnya akan tersedia bahkan setelah asynchronous pekerjaan.

Saya menggunakan pendekatan ini banyak. Aku akan tertarik untuk mengetahui bagaimana pendekatan ini bekerja di mana kabel hasilnya kembali melalui berturut-turut modul yang terlibat.

Komentar (1)

Sementara janji-janji dan callback bekerja dengan baik dalam banyak situasi, itu adalah rasa sakit di bagian belakang untuk mengekspresikan sesuatu seperti:

if (!name) {
  name = async1();
}
async2(name);

Anda'd akhirnya akan melalui async1; memeriksa apakah nama undefined atau tidak dan menelepon balik yang sesuai.

async1(name, callback) {
  if (name)
    callback(name)
  else {
    doSomething(callback)
  }
}

async1(name, async2)

Sementara itu okay kecil contoh-contoh itu akan menjengkelkan ketika anda memiliki banyak kasus-kasus serupa dan penanganan kesalahan yang terlibat.

Serat membantu dalam memecahkan masalah.

var Fiber = require('fibers')

function async1(container) {
  var current = Fiber.current
  var result
  doSomething(function(name) {
    result = name
    fiber.run()
  })
  Fiber.yield()
  return result
}

Fiber(function() {
  var name
  if (!name) {
    name = async1()
  }
  async2(name)
  // Make any number of async calls from here
}

Anda dapat checkout proyek di sini.

Komentar (4)

Jawaban singkatnya adalah, anda harus mengimplementasikan callback seperti ini:

function callback(response) {
    // Here you can do what ever you want with the response object.
    console.log(response);
}

$.ajax({
    url: "...",
    success: callback
});
Komentar (0)

Berikut contoh yang saya tulis menunjukkan bagaimana untuk

  • Menangani asynchronous HTTP panggilan;
  • Menunggu respon dari masing-API call;
  • Gunakan Janji pola;
  • Gunakan Janji.semua pola untuk bergabung beberapa HTTP panggilan;

Ini contoh kerja adalah self-contained. Itu akan menentukan permintaan sederhana objek yang menggunakan jendela XMLHttpRequest objek untuk membuat panggilan. Itu akan mendefinisikan sebuah fungsi sederhana untuk menunggu banyak janji-janji yang harus diselesaikan.

Konteks. Contoh query Spotify Web API akhir dalam rangka untuk mencari playlist benda untuk satu set tertentu dari query string:

[
 "search?type=playlist&q=%22doom%20metal%22",
 "search?type=playlist&q=Adele"
]

Untuk setiap item, yang baru Berjanji akan memecat blok - ExecutionBlock, mengurai hasil, jadwal baru yang menjanjikan berdasarkan hasil array, yang merupakan daftar dari Spotify user benda-benda dan mengeksekusi baru HTTP panggilan dalam ExecutionProfileBlock asynchronously.

Anda kemudian dapat melihat bersarang Janji struktur, yang memungkinkan anda menelurkan beberapa dan benar-benar asynchronous bersarang HTTP panggilan, dan bergabung dengan hasil dari masing-masing subset dari panggilan melalui Janji.semua.

CATATAN Baru-baru Spotify pencarian Api akan memerlukan akses token akan ditentukan dalam header permintaan:

-H "Authorization: Bearer {your access token}" 

Jadi, anda untuk menjalankan contoh berikut anda harus menempatkan akses token anda di header permintaan:

var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
        document.getElementById("console").innerHTML += s + "<br/>"
    }
}

// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
        var request;
        if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // Internet Explorer
            try {
                request = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (e) {
                try {
                  request = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (e) {}
            }
        }

        // State changes
        request.onreadystatechange = function() {
            if (request.readyState === 4) { // Done
                if (request.status === 200) { // Complete
                    response(request.responseText)
                }
                else
                    response();
            }
        }
        request.open('GET', what, true);
        request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
        request.send(null);
    }
}

//PromiseAll
var promiseAll = function(items, block, done, fail) {
    var self = this;
    var promises = [],
                   index = 0;
    items.forEach(function(item) {
        promises.push(function(item, i) {
            return new Promise(function(resolve, reject) {
                if (block) {
                    block.apply(this, [item, index, resolve, reject]);
                }
            });
        }(item, ++index))
    });
    Promise.all(promises).then(function AcceptHandler(results) {
        if (done) done(results);
    }, function ErrorHandler(error) {
        if (fail) fail(error);
    });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
    var url = "https://api.spotify.com/v1/"
    url += item;
    console.log( url )
    SimpleRequest.call(url, function(result) {
        if (result) {

            var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
                return item.owner.href;
            })
            resolve(profileUrls);
        }
        else {
            reject(new Error("call error"));
        }
    })
}

arr = [
    "search?type=playlist&q=%22doom%20metal%22",
    "search?type=playlist&q=Adele"
]

promiseAll(arr, function(item, index, resolve, reject) {
    console.log("Making request [" + index + "]")
    ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results

    console.log("All profiles received " + results.length);
    //console.log(JSON.stringify(results[0], null, 2));

    ///// promiseall again

    var ExecutionProfileBlock = function(item, index, resolve, reject) {
        SimpleRequest.call(item, function(result) {
            if (result) {
                var obj = JSON.parse(result);
                resolve({
                    name: obj.display_name,
                    followers: obj.followers.total,
                    url: obj.href
                });
            } //result
        })
    } //ExecutionProfileBlock

    promiseAll(results[0], function(item, index, resolve, reject) {
        //console.log("Making request [" + index + "] " + item)
        ExecutionProfileBlock(item, index, resolve, reject);
    }, function(results) { // aggregated results
        console.log("All response received " + results.length);
        console.log(JSON.stringify(results, null, 2));
    }

    , function(error) { // Error
        console.log(error);
    })

    /////

  },
  function(error) { // Error
      console.log(error);
  });
<div id="console" />

Saya telah banyak membahas solusi ini di sini.

Komentar (0)

2017 jawaban: anda sekarang dapat melakukan apa yang anda inginkan dalam setiap browser saat ini dan node

Ini adalah cukup sederhana:

  • Kembali Janji
  • Gunakan 'menanti', yang akan memberitahu JavaScript untuk menanti janji untuk diselesaikan menjadi sebuah nilai (seperti HTTP response)
  • Tambahkan 'async' kata kunci untuk fungsi orang tua

Berikut ini's bekerja versi dari kode anda:

(async function(){

var response = await superagent.get('...')
console.log(response)

})()

menunggu didukung di semua browser yang ada saat ini dan node 8

Komentar (4)

It's masalah yang sangat umum yang kita hadapi saat berjuang dengan 'misteri' JavaScript. Biarkan aku mencoba menjelaskan misteri ini hari ini.

Let's mulai dengan sederhana fungsi JavaScript:

function foo(){
// do something 
 return 'wohoo';
}

let bar = foo(); // bar is 'wohoo' here

Yang's sederhana sinkron fungsi panggilan (di mana setiap baris dari kode 'selesai dengan tugasnya' sebelum yang berikutnya secara berurutan), dan hasilnya sama seperti yang diharapkan.

Sekarang mari's menambahkan sedikit twist, dengan memperkenalkan sedikit keterlambatan dalam fungsi kita, sehingga semua baris kode tidak 'selesai' dalam urutan. Dengan demikian, itu akan meniru perilaku asinkron fungsi :

function foo(){
 setTimeout( ()=>{
   return 'wohoo';
  }, 1000 )
}

let bar = foo() // bar is undefined here

Jadi di sana anda pergi, bahwa penundaan hanya patah fungsi yang kita harapkan! Tapi apa yang sebenarnya terjadi ? Nah, itu's benar-benar cukup logis jika anda melihat kode. fungsi foo(), setelah eksekusi, mengembalikan apa-apa (dengan demikian kembali nilai undefined), tetapi tidak memulai timer, yang menjalankan fungsi setelah 1s untuk kembali 'wohoo'. Tapi seperti yang anda lihat, nilai yang's ditugaskan untuk bar adalah segera kembali barang-barang dari foo(), tidak apa-apa lagi yang datang kemudian.

Jadi, bagaimana kita mengatasi masalah ini?

Let's meminta kami fungsi untuk JANJI. Janji ini benar-benar tentang apa itu berarti : hal itu berarti bahwa fungsinya adalah untuk memberikan output apapun itu akan di masa depan. jadi let's melihat dalam tindakan untuk kami sedikit masalah di atas :

function foo(){
   return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
    setTimeout ( function(){ 
      // promise is RESOLVED , when execution reaches this line of code
       resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
    }, 1000 )
  })
}

let bar ; 
foo().then( res => {
 bar = res;
 console.log(bar) // will print 'wohoo'
});

Dengan demikian, ringkasan - untuk mengatasi asynchronous fungsi seperti ajax berdasarkan panggilan dll., anda dapat menggunakan berjanji untuk menyelesaikan nilai (yang anda berniat untuk kembali). Dengan demikian, dalam waktu singkat anda mengatasi nilai bukan kembali ke, di asinkron fungsi.

UPDATE (janji-Janji dengan async/menginap)

Selain menggunakan lalu/menangkap untuk bekerja dengan janji-janji, tidak ada satu pendekatan yang lebih. Idenya adalah untuk mengenali asynchronous fungsi dan kemudian menunggu janji-janji untuk menyelesaikan, sebelum pindah ke baris berikutnya kode. It's masih hanya janji-janji di bawah tenda, tapi dengan berbagai pendekatan sintaksis. Untuk membuat hal-hal yang lebih jelas, anda dapat menemukan perbandingan di bawah ini:

kemudian/catch versi:

function saveUsers(){
     getUsers()
      .then(users => {
         saveSomewhere(users);
      })
      .catch(err => {
         throw err;
       })
 }

async/menunggu versi:

  async function saveUsers(){
     try{
        let users = await getUsers()
        saveSomewhere(users);
     }
     catch(err){
        throw err;
     }
  }
Komentar (2)

Anda dapat menggunakan kustom ini pustaka (ditulis dengan menggunakan Janji) untuk membuat panggilan jarak jauh.

function $http(apiConfig) {
    return new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open(apiConfig.method, apiConfig.url);
        client.send();
        client.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                // Performs the function "resolve" when this.status is equal to 2xx.
                // Your logic here.
                resolve(this.response);
            }
            else {
                // Performs the function "reject" when this.status is different than 2xx.
                reject(this.statusText);
            }
        };
        client.onerror = function () {
            reject(this.statusText);
        };
    });
}

Sederhana contoh penggunaan:

$http({
    method: 'get',
    url: 'google.com'
}).then(function(response) {
    console.log(response);
}, function(error) {
    console.log(error)
});
Komentar (0)

Solusi lain adalah untuk mengeksekusi kode melalui berurutan pelaksana nsynjs.

Jika fungsi ini promisified

nsynjs akan mengevaluasi semua janji-janji secara berurutan, dan menempatkan janji hasilnya menjadi data property:

function synchronousCode() {

    var getURL = function(url) {
        return window.fetch(url).data.text().data;
    };

    var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
    console.log('received bytes:',getURL(url).length);

};

nsynjs.run(synchronousCode,{},function(){
    console.log('synchronousCode done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

Jika fungsi ini tidak promisified

Langkah 1. Bungkus dengan fungsi callback ke nsynjs sadar-wrapper (jika memiliki promisified versi, anda dapat melewatkan langkah ini):

var ajaxGet = function (ctx,url) {
    var res = {};
    var ex;
    $.ajax(url)
    .done(function (data) {
        res.data = data;
    })
    .fail(function(e) {
        ex = e;
    })
    .always(function() {
        ctx.resume(ex);
    });
    return res;
};
ajaxGet.nsynjsHasCallback = true;

Langkah 2. Menempatkan logika sinkron ke dalam fungsi:

function process() {
    console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}

Langkah 3. Menjalankan fungsi secara sinkron melalui nsynjs:

nsynjs.run(process,this,function () {
    console.log("synchronous function finished");
});

Nsynjs akan mengevaluasi semua operator dan ekspresi langkah-demi-langkah, berhenti eksekusi dalam kasus jika hasil dari beberapa memperlambat fungsi yang tidak siap.

Banyak lagi contoh-contoh berikut ini: https://github.com/amaksr/nsynjs/tree/master/examples

Komentar (1)

Js adalah satu ulir.

Browser dapat dibagi menjadi tiga bagian:

1)Event Loop

2)Web API

3)Acara Antrian

Loop acara berjalan untuk selamanya aku.e jenis infinite loop.Peristiwa Antrian adalah tempat di mana semua fungsi yang mendorong pada beberapa peristiwa(contoh:click) ini adalah satu-persatu dibawa keluar dari antrian dan dimasukkan ke dalam loop Acara yang menjalankan fungsi ini dan mempersiapkan diri untuk yang berikutnya setelah yang pertama dilaksanakan.Ini berarti Pelaksanaan salah satu fungsi doesn't dimulai sampai fungsi sebelum di antrian dieksekusi dalam acara loop.

Sekarang mari kita berpikir kita mendorong dua fungsi dalam antrian adalah untuk mendapatkan data dari server dan lain memanfaatkan data tersebut.Kami mendorong serverRequest() fungsi dalam antrian pertama maka utiliseData() fungsi. serverRequest fungsi pergi dalam acara loop dan membuat panggilan ke server karena kita tidak pernah tahu berapa banyak waktu yang dibutuhkan untuk mendapatkan data dari server jadi proses ini diperkirakan akan memakan waktu dan jadi kita sibuk kami event loop dengan demikian gantung halaman kami, yang's dimana Web API datang ke peran itu mengambil ini fungsi dari loop acara dan penawaran dengan server membuat acara loop gratis sehingga kita dapat melaksanakan fungsi berikutnya dari antrian.Fungsi berikutnya dalam antrian adalah utiliseData() yang berjalan di lingkaran tetapi karena tidak ada data yang tersedia itu pergi limbah dan pelaksanaan fungsi berikutnya berlanjut sampai akhir antrian.(Ini disebut Async menelepon saya.e kita dapat melakukan sesuatu yang lain sampai kita mendapatkan data)

Biarkan kira kami serverRequest() fungsi telah kembali pernyataan dalam kode, ketika kita mendapatkan kembali data dari server Web API akan mendorong dalam antrian pada akhir antrian. Karena itu bisa mendorong pada akhir di antrian kita tidak bisa memanfaatkan data karena tidak ada fungsi yang tersisa di antrian kami untuk memanfaatkan data ini.Dengan demikian hal ini tidak mungkin untuk kembali sesuatu dari Async Panggilan.

Jadi Solusi untuk ini adalah panggil ulang atau janji.

Gambar dari salah satu jawaban berikut ini, menjelaskan dengan Benar callback digunakan... Kami memberikan fungsi(function memanfaatkan data yang dikembalikan dari server) untuk memanggil fungsi server.

 function doAjax(callbackFunc, method, url) {
  var xmlHttpReq = new XMLHttpRequest();
  xmlHttpReq.open(method, url);
  xmlHttpReq.onreadystatechange = function() {

      if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
        callbackFunc(xmlHttpReq.responseText);
      }

  }
  xmlHttpReq.send(null);

}

Dalam Kode saya hal ini disebut sebagai

function loadMyJson(categoryValue){
  if(categoryValue==="veg")
  doAjax(print,"GET","http://localhost:3004/vegetables");
  else if(categoryValue==="fruits")
  doAjax(print,"GET","http://localhost:3004/fruits");
  else 
  console.log("Data not found");
}

Baca di sini untuk metode baru dalam ECMA(2016/17) untuk membuat async panggilan(@Felix Kling Jawaban di Atas) https://stackoverflow.com/a/14220323/7579856

Komentar (0)