Apakah janji tidak pernah teratasi menyebabkan kebocoran memori?

92

Saya punya Promise. Saya membuatnya untuk membatalkan permintaan AJAX jika diperlukan. Tetapi karena saya tidak perlu membatalkan AJAX itu, saya tidak pernah menyelesaikannya dan AJAX berhasil diselesaikan.

Cuplikan yang disederhanakan:

var defer = $q.defer();
$http({url: 'example.com/some/api', timeout: defer.promise}).success(function(data) {
    // do something
});

// Never defer.resolve() because I don't need to cancel that ajax. What happens to this promise after request?

Apakah janji yang tidak pernah terwujud menyebabkan kebocoran memori? Apakah Anda punya saran tentang cara mengelola Promisesiklus hidup?

Umut Benzer
sumber
4
Janji yang "tidak pernah terselesaikan" masih bisa "ditolak". Kata yang Anda cari adalah "tidak terpenuhi".
Steven Vachon
$ http adalah contoh yang menarik karena pada akhirnya permintaan HTTP akan habis waktu (atau sebaliknya menghasilkan respons kesalahan), jika klien tidak dapat menjangkau server, terlepas dari janji yang diteruskan ke argumen 'batas waktu'.
ryanwebjackson

Jawaban:

145

Nah, saya berasumsi Anda tidak menyimpan referensi eksplisit karena itu akan memaksanya untuk tetap dialokasikan.

Tes paling sederhana yang dapat saya pikirkan adalah mengalokasikan banyak janji dan tidak menyelesaikannya:

var $q = angular.injector(["ng"]).get("$q");
setInterval(function () {
    for (var i = 0; i < 100; i++) {
        var $d = $q.defer();
        $d.promise;
    }
}, 10);

Dan kemudian perhatikan heap itu sendiri. Seperti yang dapat kita lihat di alat profil Chrome, ini mengakumulasikan memori yang dibutuhkan untuk mengalokasikan 100 janji dan kemudian hanya "tetap di sana" kurang dari 15 megabyes untuk seluruh halaman JSFIddle

masukkan deskripsi gambar di sini

Dari sisi lain, jika kita melihat $qkode sumbernya

Kita dapat melihat bahwa tidak ada referensi dari titik global ke suatu promise tertentu, melainkan hanya dari satu promise ke callback-nya. Kode tersebut sangat mudah dibaca dan jelas. Mari kita lihat bagaimana jika Anda memiliki referensi dari callback ke promise.

var $q = angular.injector(["ng"]).get("$q");
console.log($q);
setInterval(function () {
    for (var i = 0; i < 10; i++) {
        var $d = $q.defer();
        (function ($d) { // loop closure thing
            $d.promise.then(function () {
                console.log($d);
            });
        })($d);
    }
}, 10);

masukkan deskripsi gambar di sini

Jadi setelah alokasi awal - sepertinya itu bisa mengatasinya juga :)

Kita juga bisa melihat beberapa pola GC yang menarik jika kita membiarkan contoh terakhirnya berjalan beberapa menit lagi. Kami dapat melihat bahwa ini membutuhkan waktu - tetapi dapat membersihkan callback.

masukkan deskripsi gambar di sini

Singkatnya - setidaknya di browser modern - Anda tidak perlu khawatir tentang janji yang belum terselesaikan selama Anda tidak memiliki referensi eksternal ke sana

Benjamin Gruenbaum
sumber
8
Bukankah ini berarti bahwa jika sebuah janji membutuhkan waktu terlalu lama untuk diselesaikan (tetapi pada akhirnya akan diselesaikan), itu berisiko menjadi GC?
w. Brian
6
@ w.brian kecuali Anda menetapkannya di suatu tempat - misalnya ke variabel: var b = $http.get(...)atau tambahkan callback ke sana. Itu juga memiliki referensi untuk itu. Jika sesuatu menyelesaikannya (seperti yang Anda katakan - terlalu lama untuk diselesaikan berarti tekad) - itu harus memiliki referensi padanya. Jadi ya - ini tidak akan di-GC
Benjamin Gruenbaum
3
Gotcha, itulah yang kupikirkan. Jadi, pertanyaannya adalah "Apakah janji yang tidak pernah terselesaikan menyebabkan kebocoran memori?" Jawabannya, untuk kasus penggunaan umum di mana callback diteruskan ke promise adalah ya. Baris dalam jawaban Anda ini tampaknya bertentangan dengan: "Kami juga dapat melihat beberapa pola GC yang menarik jika kami membiarkan contoh terakhirnya berjalan selama beberapa menit. Kami dapat melihat bahwa ini membutuhkan waktu - tetapi dapat membersihkan callback. " Maaf jika saya terlalu bertele-tele dan rewel, saya hanya mencoba untuk memastikan bahwa saya memahami ini.
w. Brian
1
Itu sepertinya tidak masuk akal bagi saya. Jika saya telah membuat 100.000 janji bahwa console.log () membuat beberapa baris. Saya ingin 100.000 itu untuk mencatat garis-garis itu jika mereka tiba-tiba diselesaikan dengan sihir. Atau apakah Anda mengatakan bahwa browser akan tahu bahwa ini tidak akan pernah menyelesaikan, karena baik saya maupun browser yang sebenarnya tidak memiliki referensi ke sana (tidak ada yang memengaruhinya) - jadi bagaimana mungkin itu benar? (hmm, saya bisa lihat itu mungkin benar)
odinho - Velmont
9
Ada beberapa kebenaran dalam komentar ini dan beberapa menyesatkan, jadi izinkan saya mengklarifikasi: Sebuah janji dengan penangan terlampir mungkin memenuhi syarat untuk pengumpulan sampah. Promise tetap aktif (tidak memenuhi syarat GC) jika salah satu dari hal berikut ini benar: (1) ada referensi ke objek promise, (2) ada referensi ke status "ditangguhkan" dari promise (objek / fungsi yang Anda gunakan untuk menyelesaikan / menolaknya). Di luar ini, janji memenuhi syarat untuk GC. (Jika tidak ada yang memiliki janji dan tidak ada yang dapat mengubah keadaannya, apa lagi tujuannya?)
cdhowie