Bagaimana untuk mengembalikan respons daripada panggilan async?
P粉668113768
P粉668113768 2023-08-23 12:49:32
0
2
572
<p>Bagaimana cara saya mengembalikan respons/hasil daripada fungsi <kod>foo</kod> </p> <p>Saya cuba mengembalikan nilai daripada panggilan balik dan memberikan hasilnya kepada pembolehubah setempat dalam fungsi dan mengembalikan pembolehubah itu, tetapi tiada satu pun daripada kaedah ini benar-benar mengembalikan respons - semuanya mengembalikan <kod>undefined< atau pembolehubah Nilai awal <code>result</code>. </kod></p><kod> <p><strong>Contoh fungsi tak segerak yang menerima panggilan balik</strong> (menggunakan fungsi <kod>ajax</kod> jQuery): </p> <pre class="brush:php;toolbar:false;">function foo() { hasil var; $.ajax({ url: '...', kejayaan: fungsi (tindak balas) { hasil = respons; // membalas respons; // <- Saya juga mencubanya } }); pulangan hasil; // Ia sentiasa mengembalikan `tidak ditentukan` }</pre> <p><strong>Contoh menggunakan Node.js:</strong></p> <pre class="brush:php;toolbar:false;">function foo() { hasil var; fs.readFile("path/to/file", function(err, data) { hasil = data; // kembalikan data; // <- Saya cuba yang itu juga }); pulangan hasil; // Ia sentiasa mengembalikan `tidak ditentukan` }</pre> <p><strong>Contoh <kod>kemudian</kod> blok menggunakan Janji: </strong></p> <pre class="brush:php;toolbar:false;">function foo() { hasil var; fetch(url).then(function(respons) { hasil = respons; // membalas respons; // <- Saya juga mencubanya }); pulangan hasil; // Ia sentiasa mengembalikan `tidak ditentukan` }</pre> <p><br /></p></code>
P粉668113768
P粉668113768

membalas semua(2)
P粉334721359

Jika anda tidak menggunakan jQuery dalam kod anda, jawapan ini adalah untuk anda

Kod anda sepatutnya kelihatan seperti 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 kerja yang hebatmenulis jawapan untuk orang yang menggunakan jQuery untuk AJAX, tetapi saya memutuskan untuk menyediakan alternatif untuk orang yang tidak menggunakan jQuery.

(Nota, bagi mereka yang menggunakan fetch API baharu, Angular atau Promise, saya telah menambah satu lagi jawapan di bawah )


Masalah yang anda hadapi

Berikut adalah ringkasan ringkas "penjelasan soalan" daripada jawapan lain, jika anda tidak pasti selepas membaca ini, sila baca jawapan itu.

A dalam AJAX bermaksud asynchronous. Ini bermakna menghantar permintaan (atau sebaliknya menerima respons) dialih keluar daripada aliran pelaksanaan biasa. Dalam contoh anda, .send code>.send code> 立即返回,并且在调用您作为 success 回调传递的函数之前执行下一条语句 return result; kembali serta-merta dan pernyataan seterusnya return result; dilaksanakan sebelum memanggil fungsi yang anda lalui sebagai panggilan balik success . kod>.

Ini bermakna apabila anda kembali, pendengar yang anda tentukan belum dilaksanakan lagi, ini bermakna nilai yang anda pulangkan belum ditentukan lagi.

Berikut adalah analogi mudah:

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

(biola)

Disebabkan a=5 部分尚未执行,因此返回的 a 值为 undefined. AJAX berkelakuan sedemikian rupa sehingga anda memulangkan nilai sebelum pelayan mempunyai peluang untuk memberitahu pelayar anda apakah nilai itu.

Satu penyelesaian yang mungkin untuk masalah ini ialah semula aktif tulis kod yang memberitahu program anda apa yang perlu dilakukan selepas pengiraan 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 dipanggil CPS. Pada asasnya, kami lulus getFive tindakan untuk dilakukan apabila selesai dan kami memberitahu kod kami cara bertindak balas apabila acara selesai (seperti panggilan AJAX kami, atau dalam kes ini, tamat masa).

Penggunaan ialah:

getFive(onComplete);

"5" akan muncul pada skrin. (biola).

Penyelesaian yang mungkin

Pada asasnya terdapat dua cara untuk menyelesaikan masalah ini:

  1. Jadikan panggilan AJAX segerak (kami memanggilnya SJAX).
  2. Faktor semula kod anda untuk berfungsi dengan betul dengan panggilan balik.

1. AJAX segerak - Jangan lakukannya! !

Bagi AJAX segerak, Jangan lakukannya! Jawapan Felix membuat beberapa hujah yang menarik tentang mengapa ini idea yang tidak baik. Secara keseluruhannya, ia membekukan penyemak imbas pengguna sehingga pelayan mengembalikan respons dan mencipta pengalaman pengguna yang sangat buruk. Berikut adalah satu lagi ringkasan ringkas daripada MDN yang menerangkan sebabnya:

Jika anda terpaksa melakukan ini, anda boleh melepasi bendera. Kaedah khusus adalah seperti berikut:

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. Susun semula kod

Jadikan fungsi anda menerima panggilan balik. Dalam kod contoh, anda boleh memberitahu bagaimana foo 接受回调。我们将告诉代码当 foo bertindak balas apabila selesai.

Jadi:

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

menjadi:

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

Di sini kami menghantar fungsi tanpa nama, tetapi kami boleh menghantar rujukan kepada fungsi sedia ada dengan mudah, menjadikannya kelihatan seperti:

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

Untuk butiran lanjut tentang cara mencapai reka bentuk panggil balik jenis ini, lihat jawapan Felix.

Sekarang, mari kita tentukan foo sendiri untuk beroperasi dengan sewajarnya

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)

Kini kami mempunyai fungsi foo menerima tindakan untuk dijalankan apabila AJAX berjaya diselesaikan. Kita boleh melanjutkan fungsi ini dengan menyemak sama ada status respons bukan 200 dan mengambil tindakan yang sesuai (mewujudkan pengendali kegagalan, dsb.). Ia berkesan menyelesaikan masalah kami.

Jika anda masih menghadapi masalah memahami perkara ini, Baca AJAX untuk mendapatkan panduan untuk memulakan MDN.

P粉642920522

Soalan

A dalam Ajax bermaksud asynchronous. Ini bermakna menghantar permintaan (atau sebaliknya menerima respons) dialih keluar daripada aliran pelaksanaan biasa. Dalam contoh anda, $.ajax 立即返回,并且下一条语句 return result; 在您作为 success panggilan balik dihantar sebelum fungsi dipanggil.

Berikut adalah analogi yang diharapkan dapat menjadikan perbezaan antara strim segerak dan tak segerak lebih jelas:

Segerakkan

Bayangkan anda menghubungi rakan dan memintanya mencari maklumat untuk anda. Walaupun mungkin mengambil sedikit masa, anda menunggu melalui telefon, merenung ke angkasa, sehingga rakan anda memberikan jawapan yang anda perlukan.

Perkara yang sama berlaku apabila anda membuat panggilan fungsi yang mengandungi kod "biasa":

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

var item = findItem();

// Do something with item
doSomethingElse();

Walaupun sebarang kod selepas findItem 可能需要很长时间才能执行,但 var item = findItem(); mesti tunggu sehingga fungsi mengembalikan hasilnya.

Asynchronous

Anda menghubungi rakan anda sekali lagi atas sebab yang sama. Tetapi kali ini anda memberitahunya bahawa anda cemas dan dia harus menghubungi anda semula menggunakan telefon bimbit anda. Anda menutup telefon, meninggalkan rumah, dan melakukan apa yang anda rancangkan. Sebaik sahaja rakan anda menghubungi anda semula, anda sedang memproses maklumat yang dia berikan kepada anda.

Inilah yang berlaku apabila anda membuat permintaan Ajax.

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

Tidak menunggu jawapan, tetapi meneruskan pelaksanaan serta-merta dan melaksanakan pernyataan selepas panggilan Ajax. Untuk akhirnya mendapat respons, anda perlu menyediakan fungsi yang dipanggil selepas respons diterima, panggilan balik (perasan apa-apa? Panggilan balik?). Sebarang pernyataan selepas panggilan ini akan dilaksanakan sebelum panggilan balik dipanggil.


Penyelesaian

Hayati sifat tak segerak JavaScript! Walaupun sesetengah operasi tak segerak menyediakan rakan sejawatan segerak (seperti juga "Ajax"), penggunaannya secara amnya tidak digalakkan, terutamanya dalam konteks penyemak imbas.

Kenapa teruk, awak tanya?

JavaScript berjalan dalam urutan UI penyemak imbas, dan sebarang proses yang berjalan lama boleh mengunci UI, menjadikannya tidak bertindak balas. Di samping itu, terdapat had atas pada masa pelaksanaan JavaScript dan penyemak imbas akan bertanya kepada pengguna sama ada untuk meneruskan pelaksanaan.

Semua ini membawa kepada pengalaman pengguna yang sangat teruk. Pengguna tidak akan dapat mengetahui sama ada semuanya berfungsi dengan betul. Di samping itu, kesannya akan menjadi lebih teruk bagi pengguna dengan kelajuan Internet yang lebih perlahan.

Di bawah ini kami membentangkan tiga penyelesaian berbeza yang semuanya membina antara satu sama lain:

  • Janji dengan async/await (ES2017+, berfungsi dalam pelayar lama jika anda menggunakan transpiler atau penjana semula)
  • Panggil balik (popular dalam nod)
  • Dengan then() 的 Promise (ES2015+, berfungsi dalam penyemak imbas lama jika anda menggunakan salah satu daripada banyak perpustakaan Promise)

Tiga ciri ini tersedia dalam penyemak imbas semasa dan Node 7+.


ES2017+: Gunakan async/await 进行承诺

Keluaran ECMAScript 2017 memperkenalkan sokongan peringkat sintaks untuk fungsi tak segerak. Dengan asyncawait anda boleh menulis secara tak segerak dalam "gaya segerak". Kod tersebut masih tidak segerak, tetapi lebih mudah dibaca/difahami.

async/await 构建在 Promise 之上:async 函数始终返回 Promise。 await Dibina atas Janji: Fungsi

async sentiasa mengembalikan Janji.

tunggu "membuka" Janji dan sama ada menghasilkan nilai yang Janji selesaikan, atau melemparkan ralat jika Janji ditolak. async 函数或 JavaScript 模块。模块外部不支持顶级 await,因此您可能必须创建异步 IIFE (立即调用函数表达式)来启动异步PENTING

: Anda hanya boleh menggunakan

tunggu dalam fungsi async atau modul JavaScript. Peringkat teratas tunggu tidak disokong di luar modul, jadi anda mungkin perlu mencipta IIFE tak segerak (Segera menggunakan ungkapan fungsi ) untuk memulakan konteks async (jika tidak menggunakan modul). Anda boleh membaca tentang

async

dan findItem()menunggu

. Berikut ialah contoh yang memperincikan fungsi tertunda async/awaitfindItem() di 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 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);
})();
Versi pelayar dan

nod
semasa menyokong

. Anda juga boleh menukar kod anda kepada ES5 dengan bantuan regenerator

(atau alatan yang menggunakan regenerator) untuk menyokong persekitaran yang lebih lama seperti

Babel

). 🎜 🎜 🎜Biarkan fungsi menerima panggilan balik🎜🎜 🎜Panggil balik ialah apabila fungsi 1 dihantar ke fungsi 2. Fungsi 2 boleh memanggil fungsi 1 apabila ia sudah siap. Dalam konteks proses tak segerak, panggilan balik dipanggil apabila proses tak segerak selesai. Biasanya, keputusan dihantar ke panggilan balik. 🎜

Dalam contoh soalan, anda boleh membuat foo 接受回调并将其用作 success panggilan balik. Jadi ini

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

menjadi

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

Di sini kami mentakrifkan fungsi "sebaris", tetapi anda boleh lulus mana-mana rujukan fungsi:

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

foo(myCallback);

foo itu sendiri ditakrifkan seperti berikut:

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

panggil balik akan merujuk fungsi yang kami hantar ke callback 将引用我们调用时传递给 foo 的函数,并将其传递给 success。 IE。一旦Ajax请求成功,$.ajax将调用callback并将响应传递给回调(可以用result apabila kami memanggilnya dan menghantarnya kepada kejayaan. iaitu. Setelah permintaan Ajax berjaya, $.ajax akan memanggil callback dan menghantar respons kepada panggilan balik (yang boleh dirujuk dengan result, sejak begitulah cara kita mentakrifkan panggilan balik The way).

Anda juga boleh memproses respons sebelum menghantarnya ke panggilan balik:

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

Menulis kod menggunakan panggilan balik adalah lebih mudah daripada yang kelihatan. Lagipun, JavaScript dalam penyemak imbas sebahagian besarnya didorong oleh peristiwa (peristiwa DOM). Menerima respons Ajax tidak lebih daripada peristiwa. Kesukaran mungkin timbul apabila anda perlu menggunakan kod pihak ketiga, tetapi kebanyakan masalah boleh diselesaikan dengan hanya memikirkan tentang aliran aplikasi.


ES2015+: dengan then()的 Promise >

Promise API ialah ciri ECMAScript 6 (ES2015) baharu, tetapi ia sudah mempunyai sokongan penyemak imbas yang baik. Terdapat juga banyak perpustakaan yang melaksanakan API Promises standard dan menyediakan kaedah tambahan untuk memudahkan penggunaan dan komposisi fungsi tak segerak (cth., Bluebird).

Janji adalah wadah nilai masa depan. Apabila Janji menerima nilai (diselesaikan) atau dibatalkan (ditolak), ia memberitahu mana-mana "pendengar" yang ingin mengakses nilai tersebut.

Kelebihan daripada panggilan balik biasa ialah ia membolehkan anda memisahkan kod anda dan lebih mudah untuk ditulis.

Ini adalah contoh penggunaan 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);
  });
}

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).
  });
.as-console-wrapper { max-height: 100% !important; top: 0; }
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan