Rumah > hujung hadapan web > tutorial js > Cipta Janji anda sendiri dalam JavaScript

Cipta Janji anda sendiri dalam JavaScript

Barbara Streisand
Lepaskan: 2024-12-28 13:29:10
asal
971 orang telah melayarinya

Create your own Promise in JavaScript

Kenapa?

Untuk mendapatkan idea bagaimana JavaScript Promises menjalankan panggilan balik secara tidak segerak di bawah hud.

Mari kita cipta Janji kita sendiri dalam JavaScript! Kami akan mengikut spesifikasi Promise/A, yang menggariskan cara janji mengendalikan operasi async, menyelesaikan, menolak dan memastikan pengendalian rantaian dan ralat yang boleh diramal.

Untuk memastikan perkara mudah, kami akan menumpukan pada peraturan utama yang ditandai dengan ✅ dalam spesifikasi Promises/A. Ini bukan pelaksanaan penuh, tetapi versi yang dipermudahkan. Inilah yang akan kami bina:

1. Terminologi

1.1 'janji' ialah objek atau fungsi dengan kaedah kemudian yang tingkah lakunya mematuhi spesifikasi ini.

1.2 thenable' ialah objek atau fungsi yang mentakrifkan kaedah kemudian.

1.3 'nilai' ialah mana-mana nilai JavaScript yang sah (termasuk tidak ditentukan, boleh kemudian atau janji).

1.4 'pengecualian' ialah nilai yang dilemparkan menggunakan pernyataan lontaran.

1.5 'sebab' ialah nilai yang menunjukkan sebab janji ditolak.

2. Keperluan

2.1 Negara Janji

Janji mestilah dalam salah satu daripada tiga keadaan: belum selesai, ditepati atau ditolak.

2.1.1. Apabila belum selesai, janji: ✅

⟶ boleh beralih kepada sama ada keadaan dipenuhi atau ditolak.

2.1.2. Apabila ditepati, janji: ✅

⟶ tidak boleh beralih ke mana-mana negeri lain.

⟶ mesti mempunyai nilai, yang tidak boleh berubah.

2.1.3. Apabila ditolak, janji: ✅

⟶ tidak boleh beralih ke mana-mana negeri lain.

⟶ mesti ada sebab, yang mesti tak berubah.

2.2 Kaedah kemudian

Janji mesti menyediakan kaedah kemudian untuk mengakses nilai atau sebab semasa atau akhirnya.

Kaedah janji menerima dua hujah:

promise.then(onFulfilled, onRejected);
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

2.2.1. Kedua-dua onFulfilled dan onRejected ialah hujah pilihan: ✅

⟶ Jika onFulfilled bukan fungsi, ia mesti diabaikan.

⟶ Jika onRejected bukan fungsi, ia mesti diabaikan.

2.2.2. Jika onFulfilled ialah fungsi: ✅

⟶ ia mesti dipanggil selepas janji dipenuhi, dengan nilai janji sebagai hujah pertamanya.

⟶ ia tidak boleh dipanggil sebelum janji ditepati.

⟶ ia tidak boleh dipanggil lebih daripada sekali.

2.2.3. Jika onRejected ialah fungsi, ✅

⟶ ia mesti dipanggil selepas janji ditolak, dengan alasan janji sebagai hujah pertamanya.

⟶ ia tidak boleh dipanggil sebelum janji ditolak.

⟶ ia tidak boleh dipanggil lebih daripada sekali.

2.2.4. onFulfilled atau onRejected tidak boleh dipanggil sehingga tindanan konteks pelaksanaan hanya mengandungi kod platform. ✅

2.2.5. onFulfilled dan onRejected mesti dipanggil sebagai fungsi (iaitu tanpa nilai ini). ✅

2.2.6. kemudian boleh dipanggil beberapa kali pada janji yang sama. ✅

⟶ Jika/apabila janji ditepati, semua panggilan balik onFulfilled masing-masing mesti dilaksanakan mengikut urutan panggilan asalnya pada masa itu.

⟶ Jika/apabila janji ditolak, semua panggilan balik onRejected masing-masing mesti dilaksanakan mengikut urutan panggilan asalnya pada masa itu.

2.2.7. maka mesti membalas janji. ✅

promise.then(onFulfilled, onRejected);
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

⟶ Jika sama ada onFulfilled atau onRejected mengembalikan nilai x, jalankan Prosedur Penyelesaian Janji [[Resolve]](promise2, x). ❌

⟶ Jika sama ada onFulfilled atau onRejected melemparkan pengecualian e, promise2 mesti ditolak dengan e sebagai alasan. ❌

⟶ Jika onFulfilled bukan fungsi dan promise1 dipenuhi, promise2 mesti ditunaikan dengan nilai yang sama seperti promise1. ❌

⟶ Jika onRejected bukan fungsi dan promise1 ditolak, promise2 mesti ditolak dengan alasan yang sama seperti promise1. ❌

Perlaksanaan

Janji JavaScript mengambil fungsi pelaksana sebagai hujah, yang dipanggil serta-merta apabila Janji dibuat:

promise2 = promise1.then(onFulfilled, onRejected);
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
new Promise(excecutor);
Salin selepas log masuk
Salin selepas log masuk

Spesifikasi teras Janji/A tidak berurusan dengan cara mencipta, memenuhi atau menolak janji. Terpulang kepada anda. Tetapi pelaksanaan yang anda sediakan untuk pembinaan janji harus serasi dengan API tak segerak dalam JavaScript. Berikut ialah draf pertama kelas Promise kami:

const promise = new Promise((resolve, reject) => {
    // Runs some async or sync tasks
});
Salin selepas log masuk
Salin selepas log masuk

Peraturan 2.1 (Negara Janji) menyatakan bahawa janji mesti berada dalam salah satu daripada tiga keadaan: belum selesai, dipenuhi atau ditolak. Ia juga menerangkan perkara yang berlaku di setiap negeri ini.

Apabila dipenuhi atau ditolak, janji tidak boleh beralih ke mana-mana negeri lain. Oleh itu, kita perlu memastikan janji berada dalam keadaan belum selesai sebelum membuat sebarang peralihan:

class YourPromise {
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;

        const resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = value;
            }
        };

        const reject = reason => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;
            }
        };

        try {
            executor(resolve, reject);  // The executor function being called immediately
        } catch (error) {
            reject(error);
        }
    }
}
Salin selepas log masuk
Salin selepas log masuk

Kami sudah mengetahui bahawa keadaan awal janji belum selesai, dan kami memastikan ia kekal sehingga dipenuhi atau ditolak secara jelas:

const resolve = value => {
    if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
    }
};

const reject = reason => {
    if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
    }
};
Salin selepas log masuk
Salin selepas log masuk

Memandangkan fungsi pelaksana dipanggil serta-merta selepas instantiasi janji, kami memanggilnya dalam kaedah pembina:

this.state = 'pending';
Salin selepas log masuk
Salin selepas log masuk

Draf pertama kelas YourPromise kami selesai di sini.

Spesifikasi Promise/A kebanyakannya tertumpu pada penentuan kaedah interoperable then() . Kaedah ini membolehkan kami mengakses nilai atau sebab semasa atau akhirnya janji itu. Mari kita mendalaminya.

Peraturan 2.2 (Kaedah kemudian) menyatakan bahawa janji mesti mempunyai kaedah then(), yang menerima dua hujah:

try {
    executor(resolve, reject);
} catch (error) {
    reject(error);
}
Salin selepas log masuk
Salin selepas log masuk

Kedua-dua onFulfilled dan onRejected mesti dipanggil selepas janji dipenuhi atau ditolak, melepasi nilai janji atau alasan sebagai hujah pertama mereka jika ia adalah fungsi:

class YourPromise {
    constructor(executor) {
        // Implementation
    }

    then(onFulfilled, onRejected) {
        // Implementation
    }
}
Salin selepas log masuk
Salin selepas log masuk

Selain itu, mereka tidak boleh dipanggil sebelum janji ditunaikan atau ditolak, atau lebih daripada sekali. Kedua-dua onFulfilled dan onRejected adalah pilihan dan harus diabaikan jika ia bukan fungsi.

Jika anda melihat Peraturan 2.2, 2.2.6 dan 2.2.7, anda akan melihat bahawa janji mesti mempunyai kaedah then(), kaedah then() boleh dipanggil beberapa kali dan ia mesti mengembalikan a janji:

promise.then(onFulfilled, onRejected);
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Untuk memastikan perkara mudah, kami tidak akan berurusan dengan kelas atau fungsi yang berasingan. Kami akan mengembalikan objek janji, melepasi fungsi pelaksana:

promise2 = promise1.then(onFulfilled, onRejected);
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Dalam fungsi pelaksana, jika janji dipenuhi, kami memanggil panggilan balik onFulfilled dan menyelesaikannya dengan nilai janji. Begitu juga, jika janji ditolak, kami memanggil panggilan balik onRejected dan menolaknya dengan alasan janji itu.

Soalan seterusnya ialah apa yang perlu dilakukan dengan panggilan balik onFulfilled dan onRejected jika janji masih dalam keadaan belum selesai? Kami beratur untuk dipanggil kemudian, seperti berikut:

new Promise(excecutor);
Salin selepas log masuk
Salin selepas log masuk

Kami sudah selesai. Berikut ialah draf kedua kelas Promise kami, termasuk kaedah then():

const promise = new Promise((resolve, reject) => {
    // Runs some async or sync tasks
});
Salin selepas log masuk
Salin selepas log masuk

Di sini, kami memperkenalkan dua medan: onFulfilledCallbacks dan onRejectedCallbacks sebagai baris gilir untuk menahan panggilan balik. Baris gilir ini diisi dengan panggilan balik melalui panggilan then() semasa janji belum selesai dan ia dipanggil apabila janji ditepati atau ditolak.

Teruskan uji kelas Promise anda:

class YourPromise {
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;

        const resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = value;
            }
        };

        const reject = reason => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;
            }
        };

        try {
            executor(resolve, reject);  // The executor function being called immediately
        } catch (error) {
            reject(error);
        }
    }
}
Salin selepas log masuk
Salin selepas log masuk

Ia sepatutnya mengeluarkan:

const resolve = value => {
    if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
    }
};

const reject = reason => {
    if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
    }
};
Salin selepas log masuk
Salin selepas log masuk

Sebaliknya, jika anda menjalankan ujian berikut:

this.state = 'pending';
Salin selepas log masuk
Salin selepas log masuk

Anda akan mendapat:

try {
    executor(resolve, reject);
} catch (error) {
    reject(error);
}
Salin selepas log masuk
Salin selepas log masuk

Sebaliknya:

class YourPromise {
    constructor(executor) {
        // Implementation
    }

    then(onFulfilled, onRejected) {
        // Implementation
    }
}
Salin selepas log masuk
Salin selepas log masuk

Kenapa? Isunya terletak pada cara kaedah then() memproses panggilan balik apabila contoh YourPromise sudah diselesaikan atau ditolak pada masa then() dipanggil. Khususnya, apabila keadaan janji tidak belum selesai, kaedah then() tidak menangguhkan pelaksanaan panggilan balik dengan betul ke baris gilir tugas mikro seterusnya. Dan itu menyebabkan pelaksanaan segerak. Dalam ujian contoh kami:

⟶ Janji segera diselesaikan dengan nilai 'Segera diselesaikan'.

⟶ Apabila promise.then() dipanggil keadaan sudah dipenuhi, jadi panggilan balik onFulfilled dilaksanakan terus tanpa ditunda ke baris gilir tugas mikro seterusnya.

Di sini Peraturan 2.2.4 mula dimainkan. Peraturan ini memastikan bahawa panggil balik then() (onFulfilled atau onRejected) dilaksanakan secara tidak segerak, walaupun janji telah diselesaikan atau ditolak. Ini bermakna panggilan balik tidak boleh dijalankan sehingga timbunan pelaksanaan semasa benar-benar jelas dan hanya kod platform (seperti gelung acara atau baris gilir tugas mikro) sedang dijalankan.

Mengapa peraturan ini penting?

Peraturan ini ialah salah satu peraturan terpenting dalam spesifikasi Promise/A. Kerana ia memastikan bahawa:

⟶ Walaupun janji diselesaikan serta-merta, panggilan balik then() itu tidak akan dilaksanakan sehingga tanda seterusnya bagi gelung acara.

⟶ Tingkah laku ini sejajar dengan kelakuan API tak segerak yang lain dalam JavaScript seperti setTimeout atau process.nextTick.

Bagaimanakah kita boleh mencapai ini?

Ini boleh dicapai sama ada dengan mekanisme tugasan makro seperti setTimeout atau setImmediate, atau dengan mekanisme tugasan mikro seperti queueMicrotask atau process.nextTick. Kerana panggilan balik dalam tugasan mikro atau tugasan makro atau mekanisme serupa akan dilaksanakan selepas konteks pelaksanaan JavaScript semasa selesai.

Untuk menyelesaikan isu di atas, kami perlu memastikan bahawa walaupun keadaan telah dipenuhi atau ditolak, panggilan balik yang sepadan (onFulfilled atau onRejected) dilaksanakan secara tidak segerak menggunakan queueMicrotask. Berikut ialah pelaksanaan yang diperbetulkan:

promise.then(onFulfilled, onRejected);
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Jalankan semula kod ujian contoh sebelumnya. Anda sepatutnya mendapat output berikut:

promise2 = promise1.then(onFulfilled, onRejected);
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Itu sahaja.

Setakat ini, anda sepatutnya mempunyai pemahaman yang jelas tentang cara panggilan balik dari then() ditangguhkan dan dilaksanakan dalam baris gilir tugas mikro seterusnya, yang membolehkan gelagat tak segerak. Pemahaman yang kukuh tentang konsep ini adalah penting untuk menulis kod tak segerak yang berkesan dalam JavaScript.

Apa yang seterusnya? Memandangkan artikel ini tidak merangkumi spesifikasi penuh Janji/A, anda boleh cuba melaksanakan yang lain untuk mendapatkan pemahaman yang lebih mendalam.

Memandangkan anda telah berjaya sejauh ini, semoga anda menikmati bacaannya! Sila kongsi artikel.

Ikuti saya di:
LinkedIn, Sederhana dan Github

Atas ialah kandungan terperinci Cipta Janji anda sendiri dalam JavaScript. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan