Saya melakukan beberapa pengujian unit. Framework pengujian memuat halaman ke dalam iFrame, lalu menjalankan pernyataan pada halaman tersebut. Sebelum setiap tes dimulai, saya membuat Promise
yang menetapkan acara iFrame onload
untuk dipanggil resolve()
, menyetel iFrame src
, dan mengembalikan janji.
Jadi, saya bisa menelepon loadUrl(url).then(myFunc)
, dan itu akan menunggu halaman dimuat sebelum mengeksekusi apa pun myFunc
.
Saya menggunakan pola semacam ini di semua tempat dalam pengujian saya (tidak hanya untuk memuat URL), terutama untuk memungkinkan perubahan pada DOM terjadi (misalnya meniru mengklik tombol, dan menunggu div bersembunyi dan ditampilkan).
Kelemahan dari desain ini adalah saya terus-menerus menulis fungsi anonim dengan beberapa baris kode di dalamnya. Lebih lanjut, sementara saya memiliki work-around (QUnit's assert.async()
), fungsi pengujian yang mendefinisikan promise selesai sebelum promise dijalankan.
Saya bertanya-tanya apakah ada cara untuk mendapatkan nilai dari a Promise
atau tunggu (blokir / tidur) hingga terselesaikan, mirip dengan .NET IAsyncResult.WaitHandle.WaitOne()
. Saya tahu JavaScript adalah single-threaded, tetapi saya berharap itu tidak berarti bahwa suatu fungsi tidak dapat menghasilkan.
Intinya, adakah cara agar yang berikut ini memberikan hasil dalam urutan yang benar?
function kickOff() {
return new Promise(function(resolve, reject) {
$("#output").append("start");
setTimeout(function() {
resolve();
}, 1000);
}).then(function() {
$("#output").append(" middle");
return " end";
});
};
function getResultFrom(promise) {
// todo
return " end";
}
var promise = kickOff();
var result = getResultFrom(promise);
$("#output").append(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>
sumber
.then(fnAppend.bind(myDiv))
, yang dapat sangat mengurangi anon.Jawaban:
Generasi Javascript saat ini di browser tidak memiliki
wait()
atausleep()
yang memungkinkan hal lain untuk berjalan. Jadi, Anda tidak bisa melakukan apa yang Anda minta. Sebaliknya, ia memiliki operasi asinkron yang akan melakukan tugasnya dan kemudian memanggil Anda setelah selesai (seperti yang Anda telah gunakan untuk janji).Sebagian karena threaded tunggal Javascript. Jika utas tunggal berputar, maka Javascript lain tidak dapat mengeksekusi hingga utas pemintalan selesai. ES6 memperkenalkan
yield
dan generator yang akan memungkinkan beberapa trik kooperatif seperti itu, tetapi kami cukup jauh untuk dapat menggunakannya di banyak browser yang diinstal (mereka dapat digunakan dalam beberapa pengembangan sisi server di mana Anda mengontrol mesin JS yang sedang digunakan).Pengelolaan kode berbasis janji yang cermat dapat mengontrol urutan eksekusi untuk banyak operasi asinkron.
Saya tidak yakin saya mengerti persis urutan apa yang Anda coba capai dalam kode Anda, tetapi Anda dapat melakukan sesuatu seperti ini menggunakan
kickOff()
fungsi yang ada , dan kemudian memasang.then()
penangan padanya setelah memanggilnya:Ini akan mengembalikan keluaran dalam urutan terjamin - seperti ini:
Pembaruan pada 2018 (tiga tahun setelah jawaban ini ditulis):
Jika Anda mentranspilasi kode Anda atau menjalankan kode Anda di lingkungan yang mendukung fitur ES7 seperti
async
danawait
, Anda sekarang dapat menggunakanawait
untuk membuat kode Anda "muncul" untuk menunggu hasil janji. Itu masih berkembang dengan janji. Itu masih tidak memblokir semua Javascript, tetapi memungkinkan Anda untuk menulis operasi berurutan dalam sintaks yang lebih bersahabat.Alih-alih cara ES6 dalam melakukan sesuatu:
Kamu bisa melakukan ini:
sumber
then()
adalah apa yang selama ini saya lakukan. Saya hanya tidak suka menulisfunction() { ... }
sepanjang waktu. Itu mengacaukan kode.(foo) => { ... }
bukannyafunction(foo) { ... }
foo => single.expression(here)
untuk menghilangkan tanda kurung keriting dan bulat.async
danawait
sintaks sebagai opsi yang sekarang tersedia di node.js dan di browser modern atau melalui transpiler.Jika menggunakan ES2016 Anda dapat menggunakan
async
danawait
dan melakukan sesuatu seperti:Jika menggunakan ES2015 Anda dapat menggunakan Generator . Jika Anda tidak menyukai sintaksnya, Anda dapat mengabstraksikannya menggunakan
async
fungsi utilitas seperti yang dijelaskan di sini .Jika menggunakan ES5 Anda mungkin menginginkan perpustakaan seperti Bluebird untuk memberi Anda kontrol lebih.
Terakhir, jika runtime Anda mendukung ES2015, urutan eksekusi dapat dipertahankan dengan paralelisme menggunakan Fetch Injection .
sumber
async
/await
.async
/await
hanyalah sintaks lain untukPromise.then
.async
tidak akan menunggu ... kecuali itu menghasilkan penggunaanawait
. Contoh ES2016 / ES7 masih tidak dapat dijalankan jadi inilah beberapa kode yang saya tulis tiga tahun lalu yang mendemonstrasikan perilakunya.Pilihan lainnya adalah menggunakan Promise.all untuk menunggu serangkaian promise diselesaikan. Kode di bawah ini menunjukkan bagaimana menunggu semua janji diselesaikan dan kemudian menangani hasil setelah semuanya siap (karena tampaknya itulah tujuan pertanyaan); Namun, start jelas bisa menghasilkan keluaran sebelum tengah diselesaikan hanya dengan menambahkan kode itu sebelum panggilan itu menyelesaikan.
sumber
Anda bisa melakukannya secara manual. (Saya tahu, itu bukan solusi yang bagus, tapi ..) gunakan
while
loop sampairesult
tidak memiliki nilaisumber
result
berubah, hasil tidak akan pernah memiliki kesempatan untuk berubah karena event loop tidak pernah bisa memproses yang lain. Dan,result
ini hanyalah argumen fungsi yang tidak pernah berubah.