React: Menghentikan `isMounted`

Dibuat pada 14 Nov 2015  ·  48Komentar  ·  Sumber: facebook/react

isMounted sudah tidak tersedia di kelas ES6, dan kami sudah memiliki peringatan yang mengatakan bahwa kami "mungkin" menghapusnya, tetapi kami sebenarnya tidak memiliki masalah github untuk menghentikannya. Sesuai diskusi kita hari ini, pada dasarnya kita sepakat bahwa kita akan mulai menjauh dari isMounted dan menghentikannya. Kami masih perlu mencari tahu beberapa cerita bagus seputar janji (dan kasus penggunaan terkait).

Masalah ini adalah untuk melacak kemajuan menuju tujuan itu.

Untuk latar belakang, silakan baca:

Komentar yang paling membantu

Metode sederhana ini dapat digunakan untuk menambahkan pembatalan ke janji apa pun

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

EDIT: Diperbarui untuk kebenaran/kelengkapan.

CARA PENGGUNAAN

const somePromise = new Promise(r => setTimeout(r, 1000));

const cancelable = makeCancelable(somePromise);

cancelable
  .promise
  .then(() => console.log('resolved'))
  .catch(({isCanceled, ...error}) => console.log('isCanceled', isCanceled));

// Cancel promise
cancelable.cancel();

Semua 48 komentar

Saya tidak setuju dengan ini. Janji ES6 khususnya tidak dapat dibatalkan dengan andal pada componentWillUnmount , jadi menghapus satu-satunya cara untuk memeriksa apakah komponen dipasang sebelum setState atau tindakan lain membuka jalan bagi banyak bug asinkron yang sulit dilacak.

@yaycmyk Jadi barisnya:

Kami masih perlu mencari tahu beberapa cerita bagus seputar janji (dan kasus penggunaan terkait).

Harap baca masalah latar belakang yang saya cantumkan, khususnya: https://github.com/facebook/react/issues/2787#issuecomment -68738793

Saya memang membaca komentarnya. Saya hanya menemukan masalah yang sulit dipecahkan.

Mengapa janji tidak dapat diandalkan dibatalkan? Adakah sumber/bukti/contoh?

Pada hari Senin, 16 November 2015, Evan Jacobs [email protected] menulis:

Saya tidak setuju dengan ini. Janji ES6 khususnya tidak dapat diandalkan
dibatalkan pada componentWillUnmount, jadi hapus satu-satunya cara untuk memeriksa apakah
komponen dipasang sebelum setState atau tindakan lain membuka
cara untuk banyak bug async yang sulit dilacak.

@nvartolomei Lihat spesifikasi janji ES6.

Ini adalah tujuan jangka panjang, bukan sesuatu yang terjadi dengan segera. Tetapi kami ingin melacak perencanaan dan diskusi di satu tempat dan tidak di seluruh komentar di setiap masalah ketika ini muncul. Kami menyadari masalah Janji yang saat ini tidak dapat dibatalkan yang merupakan alasan utama kami belum melakukan ini.

@yaycmyk Untuk menyederhanakan masalah yang sangat kompleks... komentar mengatakan... menggunakan isMounted untuk menghindari setState untuk komponen yang tidak dipasang tidak benar-benar menyelesaikan masalah yang setState peringatan mencoba untuk menunjukkan - sebenarnya, itu hanya menyembunyikan masalah. Juga, memanggil setState sebagai hasil dari janji adalah sedikit anti-pola, karena dapat menyebabkan kondisi balapan yang belum tentu muncul dalam pengujian. Jadi kami ingin menyingkirkannya, dan mencari tahu rekomendasi "praktik terbaik" untuk menggunakan janji dengan React.

Saya setuju bahwa masalahnya agak tidak dapat dipahami, tetapi itu sebagian besar karena ini adalah masalah kompleks yang masih kami cari tahu dan belum memiliki tanggapan yang jelas.

memanggil setState sebagai hasil dari janji adalah sedikit anti-pola, karena dapat menyebabkan kondisi balapan yang belum tentu muncul dalam pengujian

Kita bisa setuju untuk tidak setuju pada yang satu itu. Ada kalanya konten diambil secara asinkron dan Anda tidak ingin harus melalui perenderan skala penuh untuk memasukkan konten itu setelah diselesaikan. Saya menggunakannya secara khusus dalam implementasi tampilan tabel tak terbatas di mana rendering virtual penuh tidak diperlukan.

Anda mungkin tidak dapat membatalkan janji, tetapi Anda dapat membuatnya mereferensikan komponen saat unmount, seperti:

const SomeComponent = React.createClass({
    componentDidMount() {
        this.protect = protectFromUnmount();

        ajax(/* */).then(
            this.protect( // <-- barrier between the promise and the component
                response => {this.setState({thing: response.thing});}
            )
        );
    },
    componentWillUnmount() {
        this.protect.unmount();
    },
});

Perbedaan penting adalah ketika this.protect.unmount() dipanggil componentWillUnmount , semua panggilan balik di-dereference, artinya komponen di-dereferensi, dan ketika janji selesai, ia hanya memanggil no-op. Ini harus mencegah kebocoran memori apa pun yang terkait dengan janji referensi komponen yang dilepas. sumber untuk protectFromUnmount

Metode sederhana ini dapat digunakan untuk menambahkan pembatalan ke janji apa pun

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

EDIT: Diperbarui untuk kebenaran/kelengkapan.

CARA PENGGUNAAN

const somePromise = new Promise(r => setTimeout(r, 1000));

const cancelable = makeCancelable(somePromise);

cancelable
  .promise
  .then(() => console.log('resolved'))
  .catch(({isCanceled, ...error}) => console.log('isCanceled', isCanceled));

// Cancel promise
cancelable.cancel();

Membuat daftar cara untuk mendukung janji ES6 agar dapat dibatalkan adalah hal yang tidak penting. Tujuannya adalah untuk memberikan solusi yang berfungsi DENGAN spesifikasi daripada mencoba bekerja DI SEKITAR spesifikasi.

Saya setuju. Alih-alih hanya memeriksa apakah komponen tersebut masih terpasang ketika kami menerima hasil yang dijanjikan, kami harus menggunakan semua jenis sihir sehingga kami dapat "melepaskan" janji kami dari komponen yang seharusnya menetapkan hasilnya, jelas melawan cara janji di desain.
Bagi saya rasanya seperti merekayasa solusi di mana tes sederhana adalah cara termudah untuk menangani ini.

Kami dapat melakukan pemeriksaan sederhana hanya dengan:

React.createClass(function() {
  componentDidMount: function() {
    this._isMounted = true;

    ajax(/* */).then(this.handleResponse);
  }

  handleResponse: function(response) {
    if (!this._isMounted) return; // Protection

    /* */
  }

  componentWillUnmount: function() {
    this._isMounted = false;
  }
});

Ini tentu saja pendapat saya, tetapi menurut saya pemuatan data asinkron dengan janji di dalam komponen reaksi adalah skenario yang umum sehingga harus dicakup oleh reaksi, daripada harus menulis kode boilerplate kita sendiri.

Masalahnya, untuk mendapatkan status mount yang sebenarnya kita harus menambahkan listener ketika reaksi akan menyelesaikan proses mount DOM di setiap komponen (sama, yang melampirkan componentDidMount, jika didefinisikan), tetapi itu akan mempengaruhi perf, karena kita tidak perlu untuk membuangnya di mana-mana. Komponen jangan dengarkan pemasangan DOM siap secara default karena componentDidMount tidak ditentukan.

Bagaimana jika setState dapat diberikan janji berantai yang menyelesaikan perubahan status yang diinginkan? Jika komponen dilepas, maka jika ada janji yang tertunda, hasil akhirnya akan diabaikan.

@istarkov pola yang bagus, suka! Berikut adalah API yang sedikit diubah untuk itu:

// create a new promise
const [response, cancel] = await cancelable(fetch('/api/data'));

// cancel it
cancel();

Karena saya baru mengenal Bereaksi dan membaca dokumen, hanya untuk membuang ini di luar sana: Memuat Data Awal melalui tip .isMounted() , sehingga situs web tidak setuju dengan situs web. Akan sangat bagus untuk melihat Tip lengkap tentang cara membatalkan pemuatan awal di componentWillUnmount , mungkin menggunakan pola @istarkov di atas.

@dtertman Diperbaiki di https://github.com/facebook/react/pull/5870 , akan online saat dokumen diambil alih.

@jimfb terima kasih, tidak yakin bagaimana saya melewatkannya dalam pencarian.

@istarkov tidak yakin apakah ini disengaja tetapi makeCancelable Anda tidak menangani jika janji asli gagal. Ketika janji asli ditolak, tidak ada pawang yang dipanggil.

Ini tampaknya tidak ideal karena Anda mungkin masih ingin menangani kesalahan pada janji asli.

Ini adalah proposal saya untuk makeCancelable yang menangani penolakan dalam janji asli:

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

Saya tidak yakin di mana saya berdiri jika membuat janji yang dapat dibatalkan adalah ide yang bagus, tetapi jika kita akan membuat janji yang dapat dibatalkan, kita harus mempertahankan perilaku yang mendasarinya :).

@vpontis : +1:

@istarkov posting asli Anda dirujuk di sini: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html

Ingin memperbarui posting Anda atau haruskah saya mengirim pesan kepada penulis posting?

@vpontis Terima kasih, saya akan memperbaikinya! (https://github.com/facebook/react/pull/6152)

Hai @jimfb , senang bertemu denganmu di internet!

Perbaikan bug lain dalam fungsi makeCancelable : dapat menyebabkan UnhandledPromiseRejectionWarning dalam versi simpul terbaru (terutama saat menjalankan tes dengan versi simpul baru). Salah satu perubahan pada node 6.6.0 adalah bahwa semua penolakan janji yang tidak ditangani menghasilkan peringatan. Kode yang ada dari @vpontis memiliki then dan catch pada janji dasar yang sama. Secara efektif, ini menciptakan _dua_ janji, yang hanya menangani kesuksesan, dan yang hanya menangani kesalahan. Artinya jika ada kesalahan, janji pertama akan dilihat oleh node sebagai penolakan janji yang tidak tertangani.

Cara mengatasinya cukup mudah: cukup sambungkan dua panggilan sehingga membuat satu janji dengan penangan sukses dan kesalahan. Berikut kode tetapnya:

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise
      .then((val) =>
        hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
      )
      .catch((error) =>
        hasCanceled_ ? reject({isCanceled: true}) : reject(error)
      );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

@alangpierce Itu sangat dekat dengan benar, tapi tidak cukup; jika resolve() atau reject() serempak melempar karena alasan apa pun pada janji yang diselesaikan, kedua penangan akan dipanggil.

Solusinya adalah dengan menggunakan pola .then(onFulfilled, onRejected) :

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then(
      (val) => hasCanceled_ ? reject({isCanceled: true}) : resolve(val),
      (error) => hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

bukankah solusi makeCancelable ini secara efektif sama dengan panggilan isMounted() ketika melihat poin 3 mengapa isMounted() tidak digunakan lagi:

Memanggil setState saat komponen benar-benar dilepas.
Ini adalah indikasi kuat bahwa panggilan balik asinkron tidak dibersihkan dengan benar. Sayangnya, JS API mainstream membuatnya sangat mudah untuk menghindari pembersihan callback asinkron yang menggantung.

Satu panggilan balik bukanlah masalah besar. Namun, panggilan balik itu bergantung pada objek dan panggilan balik perantara, janji, dan langganan. Jika banyak komponen Anda melakukan ini, Anda akan segera mengalami masalah memori

makeCancellable hanya membuat janji lain yang akhirnya memegang referensi ke fungsi yang menyimpan referensi ke komponen. solusi makeCancellable hanya memindahkan properti boolean isMounted ke dalam janji.

untuk menyelesaikan masalah GC, Anda harus membatalkan sesuatu saat cancel() dipanggil. jika tidak, Anda masih memiliki rantai referensi dari proses async ke komponen.

class CancellableDeferred {
  constructor(request) {
    this.deferred = $.Deferred();

    request.then((data) => {
      if (this.deferred != null) {
        this.deferred.resolve(data);
      }
    });

    request.fail((data) => {
      if (this.deferred != null) {
        this.deferred.reject(data);
      }
    });
  }

  cancel() {
    this.deferred = null;
  } 

  promise() {
    return this.deferred.promise();
  }
}

-> adalah bagaimana saya akan melakukannya dengan objek yang ditangguhkan jQuery. saya belum terlalu familiar dengan Promise API jadi saya tidak tahu bagaimana tampilannya. Juga, ini tidak menolak yang ditangguhkan ketika cancel() telah dipanggil dan yang ditangguhkan belum diselesaikan. Mungkin, orang memiliki pendapat berbeda tentang bagaimana ini harus bekerja.

jadi rantai terlihat seperti ini:

Permintaan AJAX -> Penutupan -> CancelableDeferredInstance -> JQuery Ditangguhkan -> Komponen

kemudian setelah dibatalkan tampilannya seperti ini:

Permintaan AJAX -> Penutupan -> CancelableDeferredInstance /referensi objek sekarang null/ JQuery Ditangguhkan -> Komponen

jadi Permintaan AJAX tidak lagi mencegah Komponen menjadi GCd [dengan asumsi saya belum mengacaukan implementasi di suatu tempat dengan secara tidak sengaja memegang referensi ke ditangguhkan. yay ditutup....]

Hai @benmmurphy , Saya tidak terlalu akrab dengan kompilasi sampah JS dan mungkin bekerja secara berbeda dengan Bereaksi, tetapi saya memiliki pemahaman yang berbeda.

makeCancellable memungkinkan komponen React menjadi sampah yang dikumpulkan saat dilepas. saya akan menjelaskan.

makeCancellable hanya membuat janji lain yang akhirnya memegang referensi ke fungsi yang menyimpan referensi ke komponen. solusi makeCancellable hanya memindahkan properti boolean isMounted ke dalam janji.

Tanpa makeCancellable :

handleError() {
  if (this.isMounted()) {
    console.log('ERROR')
  }
}

Dengan makeCancellable :

promise.then(...).fail((reason) => {
  if (reason.isCancelled) return;
  console.log('ERROR');
})

Tanpa makeCancellable Anda masih memiliki referensi ke this sehingga komponen tidak dapat menjadi sampah yang dikumpulkan saat dilepas. Tetapi dalam kasus lain, penangan gagal janji yang dapat dibatalkan dipanggil segera setelah komponen dilepas, jadi Anda tidak memiliki referensi yang berkeliaran lagi.

@vpontis

saya punya beberapa kode nodejs yang menggambarkan masalah. Komponen Foo hanya akan menjadi GC setelah panggilan balik asinkron resolve telah disetel ke nol. Misalnya katakanlah Anda menjalankan permintaan ajax yang membutuhkan waktu 30 detik untuk diselesaikan maka komponen dilepas. Maka komponen tidak akan GCd selama 30 detik. Ini adalah salah satu masalah yang mereka coba selesaikan dengan tidak lagi menggunakan isMount().

npm install promise
npm install weak

node --expose-gc gc.js
first gc Foo {}
after first gc Foo {}
after resolve = null Foo {}
foo gc'd
after second gc {}

https://Gist.github.com/benmmurphy/aaf35a44a6e8a1fbae1764ebed9917b6

EDIT:

maaf telah berbicara melewati Anda, tetapi pertama kali saya membaca posting itu, saya tidak mengerti maksud yang Anda coba sampaikan, tetapi sekarang saya pikir saya mengerti. saya pikir apa yang Anda coba katakan adalah karena panggilan balik kesalahan tidak mengandung referensi ke komponen (atau tidak mengikuti referensi ke komponen) maka komponen tersebut tidak dianggap direferensikan oleh janji. Ini sebenarnya benar. Nah bagian pertama benar. Namun, ada masalah dengan alasan ini:

1) meskipun pengendali kesalahan dalam contoh Anda tidak memiliki referensi ke komponen, panggilan balik then() biasanya akan dilakukan. Misalnya pegangan then biasanya akan melakukan this.setState(...) .
2) meskipun penangan kesalahan dalam contoh Anda tidak memiliki referensi ke komponen yang akan dimiliki oleh sebagian besar penangan kesalahan. misalnya mereka akan melakukan sesuatu seperti:

promise.then(...).fail((reason) => {
  if (reason.isCancelled) return;
  console.log('ERROR');

  this.setState({error: true});
})

3) meskipun kita tahu kode tidak akan mengikuti panggilan balik then() dan kita tahu itu akan keluar dari fungsi setelah memeriksa variabel isCancelled , GC tidak mengetahuinya.

dan sebelum ada yang menggunakan contoh saya atau sesuatu yang didasarkan pada itu, pastikan Anda menguji bahwa itu benar-benar GC dengan benar. saya belum menguji milik saya dan tidak akan mengejutkan saya jika itu tidak berhasil karena saya telah membuat beberapa kesalahan konyol :/

dalam hal API janji ini berfungsi untuk saya di nodejs dalam hal GC. meskipun, saya lebih suka untuk tidak memiliki parameter _resolve , _reject dekat penutupan karena saya tidak yakin apakah ini dijamin berfungsi sesuai dengan spesifikasi JS atau jika kebetulan berhasil karena node sedang melakukan beberapa optimasi. Bisakah implementasi menangkap semua variabel yang terlihat atau hanya variabel yang direferensikan dalam penutupan? Saya tidak tahu mungkin seseorang yang benar-benar mengerti JS dapat bergabung dan menjelaskan :)

var makeCancelable = (promise) => {
  let resolve;
  let reject;

  const wrappedPromise = new Promise((_resolve, _reject) => {
    resolve = _resolve;
    reject = _reject;

    promise.then((val) => {
       if (resolve != null) resolve(val)
    });
    promise.catch((error) => {
       if (reject != null) reject(error)
    });
  });

  return {
    promise: wrappedPromise,
    cancel() {
      resolve = null;
      reject = null;
    },
  };
};

Apakah fungsi isMounted akan dihapus di 16.0?

Saran untuk perbaikan kecil dengan kode @istarkov :

const makeCancelable = (promise) => {
    let hasCanceled_ = false
    promise.then((val) =>
        hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    )
    .catch((error) =>
        hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    )

    return {
        promise,
        cancel() {
            hasCanceled_ = true
        }
    }
}

Hanya saja janji baru itu mubazir.

Hanya saja janji baru itu mubazir.

@BnayaZil Anda memanggil resolve dan reject , tetapi tidak jelas dari mana asalnya . Apakah maksud Anda Promise.resolve dan Promise.reject ? Dalam hal ini, Anda masih akan mengembalikan Janji baru.

Beberapa hari yang lalu, API baru ditambahkan ke spesifikasi DOM yang memungkinkan Anda untuk membatalkan permintaan fetch(). API ini belum diimplementasikan di browser apa pun, tetapi saya telah membuat polyfill untuknya yang tersedia di NPM adalah "abortcontroller-polyfill". Polyfill pada dasarnya melakukan hal yang sama seperti kode yang diposting oleh @istarkov tetapi memungkinkan Anda untuk bertransisi tanpa perubahan kode ke API browser yang sebenarnya setelah diimplementasikan.

Detail di sini:
https://mo.github.io/2017/07/24/abort-fetch-abortcontroller-polyfill.html

Karena React.createClass tidak ada lagi di React 16 dan paket create-react-class yang baru menyertakan pesan penghentian yang jelas untuk isMounted , saya akan menutupnya.

Saya setuju dengan @benmmurphy bahwa solusi @istarkov secara efektif sama dengan menggunakan isMounted() karena tidak menyelesaikan masalah pengumpulan sampah.

Solusi @benmmurphy lebih dekat tetapi

Kuncinya adalah meneruskan fungsi melalui penutupan yang mendereferensikan penangan:

const makeCancelable = promise => {
  let cancel = () => {};

  const wrappedPromise = new Promise((resolve, reject) => {
    cancel = () => {
      resolve = null;
      reject = null;
    };

    promise.then(
      val => {
        if (resolve) resolve(val);
      },
      error => {
        if (reject) reject(error);
      }
    );
  });

  wrappedPromise.cancel = cancel;
  return wrappedPromise;
};

Penjelasan lebih lanjut tentang mengapa solusi ini memungkinkan pengumpulan sampah dan bukan solusi sebelumnya dapat ditemukan di sini .

Saya melanjutkan dan mengubah ini menjadi paket npm , trashable-react .

Sunting: buruk, saya baru saja melihat @hjylewis thrashable , dan itu membatalkan janji juga. Masih pola di bawah ini adalah peningkatan kecil IMO.

Tak satu pun dari solusi ini membatalkan Janji, yang dapat dibatalkan tanpa perpanjangan apa pun dengan menyerap janji yang tertunda selamanya.

function makeCancelable(promise) {
  let active = true;
  return {
    cancel() {active = false},
    promise: promise.then(
      value => active ? value : new Promise(()=>{}),
      reason => active ? reason : new Promise(()=>{})
    )
  }
}

// used as above:

const {promise, cancel} = makeCancelable(Promise.resolve("Hey!"))

promise.then((v) => console.log(v)) // never logs
cancel()

tinggal disini

Mungkin ada seluk-beluk untuk diselesaikan mengenai GC dan kopi belum dimulai, tetapi pola itu memastikan bahwa janji yang dikembalikan benar-benar dibatalkan dan dapat dibuat untuk tidak bocor (saya telah menerapkannya di masa lalu).

@pygy Terima kasih atas jawabannya!

Sayangnya, solusi Anda masih tidak memungkinkan untuk Pengumpulan Sampah. Anda pada dasarnya baru saja menulis ulang solusi @istarkov yang menggunakan conditional.

Anda dapat menguji ini dengan mudah dengan menjatuhkan implementasi ini ke tempat sampah dan menjalankan tes (tes pengumpulan sampah gagal).

Implementasi Anda juga gagal menangani kesalahan dengan benar.

Ini tahun 2018 apakah ada pendekatan yang lebih baik dari yang disebutkan di atas?

ya, Anda dapat menggunakan beberapa kerangka kerja navigasi yang memiliki dokumentasi dua kali ukuran reaksi asli tetapi sangat profesional

Cuplikan untuk "membatalkan" janji ini bukan IMHO yang bagus. Janji yang dibatalkan tetap tidak akan terselesaikan sampai janji asli diselesaikan. Jadi pembersihan memori tidak akan terjadi jika Anda hanya menggunakan trik isMounted.

Pembungkus janji yang dapat dibatalkan dengan benar harus menggunakan janji kedua dan Promise.race. yaitu Promise.race([originalPromise, cancelationPromise])

Solusi @benmmurphy lebih dekat tetapi

Saya pikir solusi saya berfungsi tetapi saya tidak cukup tahu tentang apa yang dijanjikan oleh runtime javascript untuk mengetahui dengan pasti. Jika Anda menjalankan solusi di bawah node di test harness Anda, itu benar GCs nilainya. Solusi saya menetapkan fungsi penyelesaian/tolak ke ruang lingkup yang lebih tinggi dan kemudian membatalkan nilai-nilai ini ketika pembatalan dipanggil. Namun, fungsi-fungsi itu masih tersedia dalam lingkup yang lebih rendah tetapi tidak direferensikan. Saya pikir mesin javascript modern tidak menangkap variabel dalam penutupan kecuali mereka direferensikan. Saya pikir ini dulunya merupakan masalah besar di mana orang secara tidak sengaja membuat kebocoran DOM karena mereka melakukan hal-hal seperti: var element = findDOM(); element.addEventListener('klik', function() {}); dan elemen akan direferensikan dalam penutupan meskipun tidak digunakan dalam penutupan.

@hjylewis @benmmurphy mengapa kita perlu dereferensi penangan ?? setelah penangan dieksekusi, pengumpulan sampah pun terjadi, kan ??

Cuplikan untuk "membatalkan" janji ini bukan IMHO yang bagus. Janji yang dibatalkan tetap tidak akan terselesaikan sampai janji asli diselesaikan. Jadi pembersihan memori tidak akan terjadi jika Anda hanya menggunakan trik isMounted.

Pembungkus janji yang dapat dibatalkan dengan benar harus menggunakan janji kedua dan Promise.race. yaitu Promise.race([originalPromise, cancelationPromise])

@hjylewis dan milik saya, apakah Anda benar-benar berfungsi, Anda dapat memverifikasinya dengan simpul yang lemah. tetapi melihat mereka lagi, saya setuju tidak satu pun dari mereka adalah kode janji tertulis yang istimewa. sebagai pengguna janji, Anda mungkin mengharapkan janji 'dibatalkan' untuk diselesaikan dalam status ditolak dan tidak satu pun dari mereka yang melakukan ini. meskipun, mungkin dalam kasus komponen ini adalah solusi yang akan lebih mudah digunakan karena Anda tidak perlu menulis kode tambahan untuk mengabaikan penangan penolakan.

saya pikir janji yang dapat ditolak yang istimewa akan menggunakan Promise.race([]) untuk membuat janji yang dapat dibatalkan. itu berfungsi karena ketika janji diselesaikan, panggilan balik yang tertunda dihapus sehingga pada titik itu tidak akan ada rantai referensi dari jaringan browser ke komponen Anda karena tidak akan ada lagi referensi antara janji balapan dan komponen.

Saya ingin tahu apakah mungkin menggunakan Promise.all() dengan janji yang dapat dibatalkan itu dan menghindari kesalahan yang tidak tertangkap di konsol browser ... karena saya hanya dapat menangkap kesalahan pembatalan pertama, yang lain tetap tidak tertangkap.

Ini tahun 2018 apakah ada pendekatan yang lebih baik dari yang disebutkan di atas?

Pendekatan apa pun yang lebih baik untuk membatalkan eksekusi janji yaitu, setTimeout, Panggilan API, dll. Ini 2019

Ada utas pembatalan Janji terjadi di TC39, (saya pikir) itu relevan di sini (mungkin .. tidak yakin)
https://github.com/tc39/proposal-cancellation/issues/24

Pendekatan apa pun yang lebih baik untuk membatalkan eksekusi janji yaitu, setTimeout, Panggilan API, dll. Ini 2019

Apakah kita mencari sesuatu seperti

const promise = new Promise(r => setTimeout(r, 1000))
  .then(() => console.log('resolved'))
  .catch(()=> console.log('error'))
  .canceled(() => console.log('canceled'));

// Cancel promise
promise.cancel();
Apakah halaman ini membantu?
0 / 5 - 0 peringkat