Rumah > Soal Jawab > teks badan
Bagaimana untuk mengembalikan respons/hasil daripada fungsi foo
yang membuat permintaan tak segerak?
Saya cuba mengembalikan nilai daripada panggilan balik dan menetapkan hasilnya kepada pembolehubah tempatan di dalam fungsi dan mengembalikan pembolehubah itu, tetapi tiada kaedah ini benar-benar mengembalikan respons - semuanya kembali undefined
或其他变量 result
dengan nilai awal .
Contoh fungsi tak segerak yang menerima panggilan balik (menggunakan fungsi ajax
jQuery):
function foo() { var result; $.ajax({ url: '...', success: function(response) { result = response; // return response; // <- I tried that one as well } }); return result; // It always returns `undefined` }
Contoh menggunakan Node.js:
function foo() { var result; fs.readFile("path/to/file", function(err, data) { result = data; // return data; // <- I tried that one as well }); return result; // It always returns `undefined` }
Contoh menggunakan then
promise block:
function foo() { var result; fetch(url).then(function(response) { result = response; // return response; // <- I tried that one as well }); return result; // It always returns `undefined` }
P粉4773692692023-10-09 09:40:11
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.
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; }
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). < /p>
Penggunaan ialah:
getFive(onComplete);
"5" akan muncul pada skrin. (biola).
Pada asasnya terdapat dua cara untuk menyelesaikan masalah ini:
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); }
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 dengan mudah menghantar rujukan kepada fungsi sedia ada, 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(); }
Kini kami mempunyai fungsi foo menerima tindakan untuk dijalankan apabila AJAX berjaya diselesaikan. Kita boleh melanjutkan fungsi ini dengan lebih lanjut 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粉5150665182023-10-09 00:20:22
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 ialah analogi yang diharapkan dapat menjadikan perbezaan antara strim segerak dan tak segerak lebih jelas:
Bayangkan anda menghubungi rakan dan minta dia 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.
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 sahaja yang anda rancang untuk lakukan. 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.
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 buruk, anda bertanya?
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:
async/await
(ES2017+, berfungsi dalam pelayar lama jika anda menggunakan transpiler atau penjana semula) 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+.
async/await 进行承诺
Keluaran ECMAScript 2017 memperkenalkan sokongan peringkat sintaks untuk fungsi tak segerak. Dengan async
和await
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
tunggu "membuka" Janji dan sama ada menghasilkan nilai yang Janji selesaikan, atau melemparkan ralat jika Janji ditolak.
async
函数或 JavaScript 模块。模块外部不支持顶级 await
,因此您可能必须创建异步 IIFE (立即调用函数表达式)来启动异步
PENTING
tunggu dalam fungsi async atau modul JavaScript. Peringkat teratas <的更多信息/code>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).
< MDN 上的 /code>Anda boleh membaca tentang
dan findItem()
menunggu
.
Berikut ialah contoh yang memperincikan fungsi tertunda async/await
findItem() 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
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 }); }
callback
将引用我们调用时传递给 foo
的函数,并将其传递给 success
。 IE。一旦Ajax请求成功,$.ajax
将调用callback
并将响应传递给回调(可以用result
apabila kami memanggilnya dan menghantarnya kepada $.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 aliran aplikasi.
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. < /p>
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; }