Cara Menulis Janji JavaScript

Apa itu janji?

Janji JavaScript adalah objek yang mewakili penyelesaian atau kegagalan tugas tak segerak dan nilai yang dihasilkannya.¹

Tamat.

Sudah tentu saya bergurau. Jadi, apakah maksud definisi itu?

Pertama sekali, banyak perkara dalam JavaScript adalah objek. Anda boleh membuat objek dengan beberapa cara yang berbeza. Cara yang paling biasa adalah dengan sintaks literal objek:

const myCar = { color: 'blue', type: 'sedan', doors: '4', };

Anda juga boleh membuatnya classdan membuat instansi dengan newkata kunci.

class Car { constructor(color, type, doors) { this.color = color; this.type = type; this.doors = doors } } const myCar = new Car('blue', 'sedan', '4');

console.log(myCar);

Janji hanyalah objek yang kita buat seperti contoh kemudian. Kami mewujudkannya dengan newkata kunci. Sebaliknya daripada tiga parameter kami diluluskan pada untuk membuat kereta kami (warna, jenis, dan pintu), kita lulus dalam fungsi yang mengambil masa dua hujah: resolvedan reject.

Pada akhirnya, janji memberitahu kita sesuatu mengenai penyelesaian fungsi tak segerak yang kita kembalikan - jika ia berfungsi atau tidak. Kami mengatakan bahawa fungsi itu berjaya dengan mengatakan janji itu diselesaikan , dan tidak berjaya dengan mengatakan janji itu ditolak.

const myPromise = new Promise(function(resolve, reject) {});

console.log(myPromise);

const myPromise = new Promise(function(resolve, reject) { resolve(10); });

Lihat, tidak terlalu menakutkan - hanya objek yang kami buat. Dan, jika kita mengembangkannya sedikit:

Di samping itu, kami dapat menyampaikan apa sahaja yang kami ingin selesaikan dan tolak. Sebagai contoh, kita dapat meneruskan objek dan bukannya rentetan:

return new Promise((resolve, reject) => { if(somethingSuccesfulHappened) { const successObject = { msg: 'Success', data,//...some data we got back } resolve(successObject); } else { const errorObject = { msg: 'An error occured', error, //...some error we got back } reject(errorObject); } });

Atau, seperti yang kita lihat sebelumnya, kita tidak perlu menyampaikan apa-apa:

return new Promise((resolve, reject) => { if(somethingSuccesfulHappend) { resolve() } else { reject(); } });

Bagaimana dengan bahagian takrif "tak segerak"?

JavaScript adalah satu utas. Ini bermaksud ia hanya dapat menjalankan satu perkara dalam satu masa. Sekiranya anda dapat membayangkan jalan raya, anda boleh menganggap JavaScript sebagai jalan raya lorong tunggal. Kod tertentu (kod tak segerak) boleh meluncur ke bahu untuk membolehkan kod lain menyebarkannya. Apabila kod tak segerak itu selesai, ia akan kembali ke jalan raya.

Sebagai catatan sampingan, kita dapat mengembalikan janji dari fungsi apa pun . Ia tidak perlu segerak. Oleh itu, janji biasanya dikembalikan dalam kes di mana fungsi yang mereka kembalikan tidak segerak. Sebagai contoh, API yang mempunyai kaedah untuk menyimpan data ke pelayan akan menjadi calon yang tepat untuk mengembalikan janji!

Pengambilan:

Janji memberi kita jalan untuk menunggu kod asinkron kami selesai, menangkap beberapa nilai darinya, dan menyampaikan nilai tersebut ke bahagian lain dari program kami.

Saya mempunyai artikel di sini yang menyelami konsep-konsep ini: Thrown For a Loop: Memahami Loop dan Timeout dalam JavaScript.

Bagaimana kita menggunakan janji?

Menggunakan janji juga disebut memakan janji. Dalam contoh di atas, fungsi kita mengembalikan objek janji. Ini membolehkan kita menggunakan kaedah berantai dengan fungsi kita.

Berikut adalah contoh kaedah rantai yang saya yakin anda pernah lihat:

const a = 'Some awesome string'; const b = a.toUpperCase().replace('ST', '').toLowerCase(); console.log(b); // some awesome ring

Sekarang, ingat janji (berpura-pura) kami:

const somethingWasSuccesful = true; function someAsynFunction() { return new Promise((resolve, reject){ if (somethingWasSuccesful) { resolve(); } else { reject() } }); }

Dan, menunaikan janji kita dengan menggunakan kaedah mengikat:

someAsyncFunction .then(runAFunctionIfItResolved(withTheResolvedValue)) .catch(orARunAfunctionIfItRejected(withTheRejectedValue));

Contoh (lebih) nyata.

Bayangkan anda mempunyai fungsi yang membuat pengguna dari pangkalan data. Saya telah menulis fungsi contoh di Codepen yang mensimulasikan API yang mungkin anda gunakan. Ini memberikan dua pilihan untuk mengakses hasilnya. Satu, anda boleh menyediakan fungsi panggilan balik di mana anda boleh mengakses pengguna atau sebarang kesalahan. Atau dua, fungsi mengembalikan janji sebagai cara untuk mengakses pengguna atau kesilapan.

Secara tradisinya, kami akan mengakses hasil kod tak segerak melalui penggunaan panggilan balik.

rr someDatabaseThing(maybeAnID, function(err, result)) { //...Once we get back the thing from the database... if(err) { doSomethingWithTheError(error) } else { doSomethingWithResults(results); } }

Penggunaan panggil balik adalah baik sehingga ia menjadi terlalu bersarang. Dengan kata lain, anda harus menjalankan lebih banyak kod tak segerak dengan setiap hasil baru. Pola callback dalam callback ini boleh menyebabkan sesuatu yang dikenali sebagai "callback hell".

Janji-janji memberi kami cara yang lebih elegan dan mudah dibaca untuk melihat aliran program kami.

doSomething() .then(doSomethingElse) // and if you wouldn't mind .catch(anyErrorsPlease);

Writing our own promise: Goldilocks, the Three Bears, and a Supercomputer

Imagine you found a bowl of soup. You’d like to know the temperature of that soup before you eat it. You're out of thermometers, but luckily, you have access to a supercomputer that tells you the temperature of the bowl of soup. Unfortunately, this supercomputer can take up to 10 seconds to get the results.

Here are a couple of things to notice.

  1. We initiate a global variable called result.
  2. We simulate the duration of the network delay with Math.random() and setTimeout().
  3. We simulate a temperature with Math.random().
  4. We keep the delay and temperature values confined within a range by adding some extra “math”. The range for temp is 1 to 300; the range for delay is 1000ms to 10000ms (1s to 10 seconds).
  5. We log the delay and temperature so we have an idea of how long this function will take and the results we expect to see when it’s done.

Run the function and log the results.

getTemperature(); console.log(results); // undefined

The temperature is undefined. What happened?

The function will take a certain amount of time to run. The variable is not set until the delay is over. So while we run the function, setTimeout is asynchronous. The part of the code in setTimeout moves out of the main thread into a waiting area.

I have an article here that dives deeper into this process: Thrown For a Loop: Understanding Loops and Timeouts in JavaScript.

Since the part of our function that sets the variable result moves into a holding area until it is done, our parser is free to move onto the next line. In our case, it’s our console.log(). At this point, result is still undefined since our setTimeout is not over.

So what else could we try? We could run getTemperature() and then wait 11 seconds (since our max delay is ten seconds) and then console.log the results.

getTemperature(); setTimeout(() => { console.log(result); }, 11000); // Too Hot | Delay: 3323 | Temperature: 209 deg

This works, but the problem with this technique is, although in our example we know the maximum network delay, in a real-life example it might occasionally take longer than ten seconds. And, even if we could guarantee a maximum delay of ten seconds, if the result is ready sooner, we are wasting time.

Promises to the Rescue

We are going to refactor our getTemperature() function to return a promise. And instead of setting the result, we will reject the promise unless the result is “Just Right,” in which case we will resolve the promise. In either case, we will pass in some values to both resolve and reject.

We can now use the results of our promise we are returning (also know as consuming the promise).

getTemperature() .then(result => console.log(result)) .catch(error => console.log(error)); // Reject: Too Cold | Delay: 7880 | Temperature: 43 deg

.then will get called when our promise resolves and will return whatever information we pass into resolve.

.catch will get called when our promise rejects and will return whatever information we pass into reject.

Most likely, you’ll consume promises more than you will create them. In either case, they help make our code more elegant, readable, and efficient.

Summary

  1. Promises are objects that contain information about the completion of some asynchronous code and any resulting values we want to pass in.
  2. To return a promise we use return new Promise((resolve, reject)=> {})
  3. To consume a promise we use .then to get the information from a promise that has resolved, and .catch to get the information from a promise that has rejected.
  4. You’ll probably use (consume) promises more than you’ll write.

References

1.) //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise