Apakah ada metode untuk menghapus instance .then
JavaScript Promise
?
Saya telah menulis kerangka pengujian JavaScript di atas QUnit . Kerangka kerja menjalankan pengujian secara sinkron dengan menjalankan masing-masing pengujian di a Promise
. (Maaf untuk panjang blok kode ini. Saya berkomentar sebaik mungkin, jadi rasanya tidak terlalu membosankan.)
/* Promise extension -- used for easily making an async step with a
timeout without the Promise knowing anything about the function
it's waiting on */
$$.extend(Promise, {
asyncTimeout: function (timeToLive, errorMessage) {
var error = new Error(errorMessage || "Operation timed out.");
var res, // resolve()
rej, // reject()
t, // timeout instance
rst, // reset timeout function
p, // the promise instance
at; // the returned asyncTimeout instance
function createTimeout(reject, tempTtl) {
return setTimeout(function () {
// triggers a timeout event on the asyncTimeout object so that,
// if we want, we can do stuff outside of a .catch() block
// (may not be needed?)
$$(at).trigger("timeout");
reject(error);
}, tempTtl || timeToLive);
}
p = new Promise(function (resolve, reject) {
if (timeToLive != -1) {
t = createTimeout(reject);
// reset function -- allows a one-time timeout different
// from the one original specified
rst = function (tempTtl) {
clearTimeout(t);
t = createTimeout(reject, tempTtl);
}
} else {
// timeToLive = -1 -- allow this promise to run indefinitely
// used while debugging
t = 0;
rst = function () { return; };
}
res = function () {
clearTimeout(t);
resolve();
};
rej = reject;
});
return at = {
promise: p,
resolve: res,
reject: rej,
reset: rst,
timeout: t
};
}
});
/* framework module members... */
test: function (name, fn, options) {
var mod = this; // local reference to framework module since promises
// run code under the window object
var defaultOptions = {
// default max running time is 5 seconds
timeout: 5000
}
options = $$.extend({}, defaultOptions, options);
// remove timeout when debugging is enabled
options.timeout = mod.debugging ? -1 : options.timeout;
// call to QUnit.test()
test(name, function (assert) {
// tell QUnit this is an async test so it doesn't run other tests
// until done() is called
var done = assert.async();
return new Promise(function (resolve, reject) {
console.log("Beginning: " + name);
var at = Promise.asyncTimeout(options.timeout, "Test timed out.");
$$(at).one("timeout", function () {
// assert.fail() is just an extension I made that literally calls
// assert.ok(false, msg);
assert.fail("Test timed out");
});
// run test function
var result = fn.call(mod, assert, at.reset);
// if the test returns a Promise, resolve it before resolving the test promise
if (result && result.constructor === Promise) {
// catch unhandled errors thrown by the test so future tests will run
result.catch(function (error) {
var msg = "Unhandled error occurred."
if (error) {
msg = error.message + "\n" + error.stack;
}
assert.fail(msg);
}).then(function () {
// resolve the timeout Promise
at.resolve();
resolve();
});
} else {
// if test does not return a Promise, simply clear the timeout
// and resolve our test Promise
at.resolve();
resolve();
}
}).then(function () {
// tell QUnit that the test is over so that it can clean up and start the next test
done();
console.log("Ending: " + name);
});
});
}
Jika tes habis waktu, Janji batas waktu saya akan assert.fail()
di tes sehingga tes ditandai sebagai gagal, yang semuanya baik dan bagus, tetapi tes terus berjalan karena tes Promise ( result
) masih menunggu untuk menyelesaikannya.
Saya perlu cara yang baik untuk membatalkan tes saya. Saya dapat melakukannya dengan membuat bidang pada modul kerangka kerja this.cancelTest
atau sesuatu, dan memeriksa sesering mungkin (misalnya pada awal setiap then()
iterasi) dalam pengujian apakah akan membatalkan. Namun, idealnya, saya dapat menggunakan $$(at).on("timeout", /* something here */)
untuk menghapus sisa then()
s pada result
variabel saya , sehingga tidak ada sisa pengujian yang dijalankan.
Apakah ada yang seperti ini?
Pembaruan Cepat
Saya mencoba menggunakan Promise.race([result, at.promise])
. Tidak berhasil.
Perbarui 2 + kebingungan
Untuk membebaskan saya, saya menambahkan beberapa baris dengan mod.cancelTest
/ polling di dalam ide pengujian. (Saya juga menghapus pemicu acara.)
return new Promise(function (resolve, reject) {
console.log("Beginning: " + name);
var at = Promise.asyncTimeout(options.timeout, "Test timed out.");
at.promise.catch(function () {
// end the test if it times out
mod.cancelTest = true;
assert.fail("Test timed out");
resolve();
});
// ...
}).then(function () {
// tell QUnit that the test is over so that it can clean up and start the next test
done();
console.log("Ending: " + name);
});
Saya menetapkan breakpoint dalam catch
pernyataan tersebut, dan itu dipukul. Yang membingungkan saya sekarang adalah then()
pernyataan itu tidak dipanggil. Ide ide?
Perbarui 3
Pikir hal terakhir. fn.call()
melakukan kesalahan yang tidak saya tangkap, jadi janji pengujian ditolak sebelum at.promise.catch()
bisa menyelesaikannya.
sumber
Prex
perpustakaan untuk pembatalan janji.Jawaban:
Tidak. Setidaknya tidak di ECMAScript 6. Promises (dan
then
penangannya) tidak dapat dibatalkan secara default (sayangnya) . Ada sedikit diskusi tentang es-diskusikan (misalnya di sini ) tentang bagaimana melakukan ini dengan cara yang benar, tetapi pendekatan apa pun yang akan menang, itu tidak akan masuk ke ES6.Sudut pandang saat ini adalah bahwa subclassing akan memungkinkan untuk membuat janji yang dapat dibatalkan menggunakan implementasi Anda sendiri (tidak yakin seberapa baik itu akan berhasil) .
Sampai komite bahasa menemukan cara terbaik (semoga ES7?) Anda masih dapat menggunakan implementasi Promise userland, banyak di antaranya pembatalan fitur.
Diskusi saat ini ada di https://github.com/domenic/cancelable-promise dan draf https://github.com/bergus/promise-cancellation .
sumber
kill
mengabaikannya dan mengabaikan dalam keadaan yang mungkin aneh efek samping telah meninggalkan lingkungan Anda (jadi Anda biasanya membuangnya juga, misalnya keluaran setengah jadi). Namun, fungsi non-pemblokiran atau asinkron dibuat untuk bekerja dalam aplikasi interaktif, di mana Anda ingin memiliki kontrol yang lebih baik atas pelaksanaan operasi yang sedang berjalan.Meskipun tidak ada cara standar untuk melakukan ini di ES6, ada pustaka bernama Bluebird untuk menangani ini.
Ada juga cara yang direkomendasikan yang dijelaskan sebagai bagian dari dokumentasi react. Ini terlihat mirip dengan apa yang Anda miliki di pembaruan ke-2 dan ke-3.
Diambil dari: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
sumber
Saya sangat terkejut bahwa tidak ada yang menyebut
Promise.race
sebagai kandidat untuk ini:sumber
cancel()
tetap akan mengakibatkan log dipanggil. `` const actualPromise = new Promise ((selesaikan, tolak) => {setTimeout (() => {console.log ('aktual dipanggil'); selesaikan ()}, 10000)}); ``then
s untuk dieksekusi), bukan bagaimana cara membatalkansetTimeout
(=>clearTimeout
) atau kode sinkron, di mana kecuali Anda meletakkan if setelah setiap baris (if (canceled) return
) ini tidak dapat dicapai. (Jangan lakukan ini)Pemakaian:
sumber
Sebenarnya tidak mungkin menghentikan pelaksanaan janji, tetapi Anda dapat membajak penolakan dan membatalkannya dari janji itu sendiri.
Pemakaian:
sumber
Ada beberapa pustaka npm untuk janji yang dapat dibatalkan.
p-cancelable https://github.com/sindresorhus/p-cancelable
janji yang bisa dibatalkan https://github.com/alkemics/CancelablePromise
sumber
Inilah implementasi kami https://github.com/permettez-moi-de-construire/cancellable-promise
Digunakan seperti
yang mana:
catch
panggilanTarik dan komentar diterima
sumber
Janji dapat dibatalkan dengan bantuan
AbortController
.Contoh:
Html
Contoh Langsung
sumber
versi sederhana :
berikan saja fungsi tolak.
solusi pembungkus (pabrik)
solusi yang saya temukan adalah dengan meneruskan objek cancel_holder. itu akan memiliki fungsi pembatalan. jika memiliki fungsi pembatalan maka ia dapat dibatalkan.
Fungsi pembatalan ini menolak janji dengan Kesalahan ('dibatalkan').
Sebelum menyelesaikan, menolak, atau on_cancel, cegah fungsi pembatalan dipanggil tanpa alasan.
Saya merasa nyaman untuk melewatkan tindakan pembatalan dengan suntikan
sumber
Coba janji-abortable : https://www.npmjs.com/package/promise-abortable
sumber
Jika kode Anda ditempatkan di kelas, Anda dapat menggunakan dekorator untuk itu. Anda memiliki dekorator seperti itu di utils-decorators (
npm install --save utils-decorators
). Ini akan membatalkan pemanggilan sebelumnya dari metode yang didekorasi jika sebelum penyelesaian panggilan sebelumnya ada panggilan lain untuk metode khusus itu.https://github.com/vlio20/utils-decorators#cancelprevious-method
sumber
Jika Anda ingin menghentikan semua hasil tangkapan agar tidak dieksekusi, Anda dapat melakukan ini dengan memasukkan janji yang tidak akan pernah terpecahkan. Ini mungkin memiliki reprokusi kebocoran memori tetapi itu akan memperbaiki masalah dan seharusnya tidak menyebabkan terlalu banyak memori yang terbuang di sebagian besar aplikasi.
sumber
Setel properti "dibatalkan" di Janji untuk memberi sinyal
then()
dancatch()
keluar lebih awal. Ini sangat efektif, terutama di Web Worker yang memiliki tugas mikro yang ada yang mengantri di Promises fromonmessage
handler.sumber
Jawaban @Michael Yagudaev berhasil untuk saya.
Tetapi jawaban asli tidak mengikat janji yang dibungkus dengan .catch () untuk menangani penanganan penolakan, berikut peningkatan saya di atas jawaban @Michael Yagudaev:
sumber
Jika p adalah variabel yang berisi Promise, maka
p.then(empty);
harus mengabaikan promise ketika akhirnya selesai atau jika sudah lengkap (ya, saya tahu ini bukan pertanyaan asli, tapi ini pertanyaan saya). "kosong" adalahfunction empty() {}
. Saya hanya pemula dan mungkin salah, tetapi jawaban lain ini tampaknya terlalu rumit. Janji seharusnya sederhana.sumber
Saya masih mengerjakan ide ini, tetapi berikut adalah cara saya menerapkan janji yang dapat dibatalkan menggunakan
setTimeout
sebagai contoh.Idenya adalah bahwa sebuah janji diselesaikan atau ditolak setiap kali Anda telah memutuskannya, jadi itu harus menjadi masalah memutuskan kapan Anda ingin membatalkan, memenuhi kriteria, dan kemudian memanggil
reject()
fungsi itu sendiri.Pertama, saya pikir ada dua alasan untuk menyelesaikan janji lebih awal: untuk menyelesaikannya dan menyelesaikannya (yang saya sebut tekad ) dan untuk membatalkan (yang saya sebut menolak ). Tentu saja, itu hanya perasaan saya. Tentu saja ada
Promise.resolve()
metode, tapi itu dalam konstruktor itu sendiri, dan mengembalikan janji terselesaikan tiruan.resolve()
Metode contoh ini sebenarnya menyelesaikan objek janji yang dibuat instance-nya.Kedua, Anda dapat dengan senang hati menambahkan apa pun yang Anda suka ke objek promise yang baru dibuat sebelum Anda mengembalikannya, jadi saya baru saja menambahkan
resolve()
danreject()
metode untuk membuatnya mandiri.Ketiga, triknya adalah dengan dapat mengakses eksekutor
resolve
danreject
fungsi nanti, jadi saya hanya menyimpannya dalam objek sederhana dari dalam closure.Saya pikir solusinya sederhana, dan saya tidak melihat ada masalah besar dengannya.
sumber