Cara Menulis Fungsi Janji Sendiri dari Awal

Pengenalan

Dalam artikel ini, anda akan belajar bagaimana menulis fungsi janji anda sendiri dari awal.

Janji membantu dalam menangani API berasaskan panggilan balik sambil mengekalkan kod yang sesuai dengan janji.

Kita hanya boleh menyelesaikan sebarang fungsi new Promise()dan tidak perlu khuatir sama sekali. Tetapi melakukan itu apabila kita mempunyai banyak fungsi akan menjadi berlebihan.

Sekiranya anda memahami janji dan panggilan balik, maka belajar bagaimana menulis fungsi janji dapat dilakukan dengan mudah. Oleh itu, mari kita mulakan.

Tetapi adakah anda pernah tertanya-tanya bagaimana janji kerja berfungsi?

Yang penting jangan berhenti menyoal. Rasa ingin tahu mempunyai alasan tersendiri untuk wujud.

- Albert Einstein

Janji diperkenalkan dalam ECMA-262 Standard, Edisi ke-6 (ES6) yang diterbitkan pada bulan Jun 2015.

Ini adalah peningkatan berbanding panggilan balik, kerana kita semua tahu betapa "neraka panggilan balik" yang tidak dapat dibaca :)

Sebagai pembangun Node.js, anda harus tahu apa itu janji dan bagaimana ia berfungsi secara dalaman, yang juga akan membantu anda dalam temu ramah JS. Jangan ragu untuk mengulasnya dengan cepat sebelum terus membaca.

Mengapa kita perlu menukar panggilan balik kepada janji?

  1. Dengan panggilan balik, jika anda ingin melakukan sesuatu secara berurutan, anda harus menentukan errargumen dalam setiap panggilan balik, yang berlebihan. Dalam janji atau async-tunggu, anda hanya boleh menambahkan .catchkaedah atau blok yang akan menangkap kesilapan yang berlaku di rantai janji
  2. Dengan panggilan balik, anda tidak dapat mengawal kapan ia dipanggil, dalam konteks apa, atau berapa kali ia dipanggil, yang boleh menyebabkan kebocoran memori.
  3. Dengan menggunakan janji, kami mengawal faktor-faktor ini (terutamanya pengendalian ralat) sehingga kodnya lebih mudah dibaca dan dikekalkan.

Cara membuat fungsi berasaskan panggilan balik mengembalikan janji

Terdapat dua cara untuk melakukannya:

  1. Bungkus fungsi dengan fungsi lain yang mengembalikan janji. Ia kemudian menyelesaikan atau menolak berdasarkan hujah panggilan balik.
  2. Promisification - Kami membuat fungsi util / helper promisifyyang akan mengubah semua API berdasarkan panggilan balik ralat pertama.

Contoh: ada API berasaskan panggilan balik yang memberikan jumlah dua nombor. Kami ingin menjanjikannya sehingga mengembalikan thenablejanji.

const getSumAsync = (num1, num2, callback) => { if (!num1 || !num2) { return callback(new Error("Missing arguments"), null); } return callback(null, num1 + num2); } getSumAsync(1, 1, (err, result) => { if (err){ doSomethingWithError(err) }else { console.log(result) // 2 } })

Bungkus dengan janji

Seperti yang anda lihat, getSumPromisemenyerahkan semua kerja ke fungsi asal getSumAsync, memberikan panggilan balik sendiri yang diterjemahkan menjadi janji resolve/reject.

Janji

Apabila kita perlu menjanjikan banyak fungsi, kita dapat membuat fungsi pembantu promisify.

Apa itu Janji?

Janji bermaksud transformasi. Ini adalah penukaran fungsi yang menerima panggilan balik ke fungsi mengembalikan janji.

Menggunakan Node.js util.promisify():

const { promisify } = require('util') const getSumPromise = promisify(getSumAsync) // step 1 getSumPromise(1, 1) // step 2 .then(result => { console.log(result) }) .catch(err =>{ doSomethingWithError(err); })

Jadi ia kelihatan seperti fungsi sihir yang berubah getSumAsyncmenjadi getSumPromiseyang mempunyai .thendan .catchkaedah

Mari tulis fungsi janji kami sendiri:

Sekiranya anda melihat langkah 1 dalam kod di atas, promisifyfungsi tersebut menerima fungsi sebagai argumen, jadi perkara pertama yang harus kita lakukan ialah menulis fungsi yang dapat melakukan hal yang sama:

const getSumPromise = myPromisify(getSumAsync) const myPromisify = (fn) => {}

Selepas itu, getSumPromise(1, 1)adalah fungsi panggilan. Ini bermaksud bahawa janji kami harus mengembalikan fungsi lain yang boleh dipanggil dengan argumen fungsi asal yang sama:

const myPromisify = (fn) => { return (...args) => { } }

Dalam kod di atas anda dapat melihat kami menyebarkan argumen kerana kami tidak tahu berapa banyak argumen yang dimiliki oleh fungsi asal. argsakan menjadi array yang mengandungi semua argumen.

Apabila anda menelefon getSumPromise(1, 1)anda sebenarnya sedang menelefon (...args)=> {}. Dalam pelaksanaan di atas, ia mengembalikan janji. Itulah sebabnya anda boleh menggunakan getSumPromise(1, 1).then(..).catch(..).

Saya harap anda mendapat petunjuk bahawa fungsi pembungkus (...args) => {}harus mengembalikan janji.

Kembalikan janji

const myPromisify = (fn) => { return (...args) => { return new Promise((resolve, reject) => { }) } }

Sekarang bahagian yang sukar adalah bagaimana memutuskan kapan resolve or rejectjanji.

Sebenarnya, itu akan ditentukan oleh getSumAsyncpelaksanaan fungsi asal - ia akan memanggil fungsi panggilan balik asal dan kita hanya perlu menentukannya. Kemudian berdasarkan errdan resultkita akan rejectatau   resolvejanji.

const myPromisify = (fn) => { return (...args) => { return new Promise((resolve, reject) => { function customCallback(err, result) { if (err) { reject(err) }else { resolve(result); } } }) } }

Kami args[]hanya terdiri daripada argumen yang dilalui getSumPromise(1, 1)kecuali fungsi panggilan balik. Jadi, anda perlu menambah customCallback(err, result)kepada args[]mana fungsi asal getSumAsyncakan memanggil sewajarnya seperti yang kita menjejaki hasil dalam customCallback.

Tolak customCallback ke args []

const myPromisify = (fn) => { return (...args) => { return new Promise((resolve, reject) => { function customCallback(err, result) { if (err) { reject(err) }else { resolve(result); } } args.push(customCallback) fn.call(this, ...args) }) } }

Seperti yang anda lihat, kami telah menambahkan fn.call(this, args), yang akan memanggil fungsi asal dalam konteks yang sama dengan argumen getSumAsync(1, 1, customCallback). Maka fungsi janji kita harus dapat resolve/rejectmengikutinya.

The above implementation will work when the original function expects a callback with two arguments, (err, result). That’s what we encounter most often. Then our custom callback is in exactly the right format and promisify works great for such a case.

But what if the original fn expects a callback with more arguments likecallback(err, result1, result2, ...)?

In order to make it compatible with that, we need to modify our myPromisify function which will be an advanced version.

const myPromisify = (fn) => { return (...args) => { return new Promise((resolve, reject) => { function customCallback(err, ...results) { if (err) { return reject(err) } return resolve(results.length === 1 ? results[0] : results) } args.push(customCallback) fn.call(this, ...args) }) } }

Example:

const getSumAsync = (num1, num2, callback) => { if (!num1 || !num2) { return callback(new Error("Missing dependencies"), null); } const sum = num1 + num2; const message = `Sum is ${sum}` return callback(null, sum, message); } const getSumPromise = myPromisify(getSumAsync) getSumPromise(2, 3).then(arrayOfResults) // [6, 'Sum is 6']

That’s all! Thank you for making it this far!

I hope you’re able to grasp the concept. Try to re-read it again. It’s a bit of code to wrap your head around, but not too complex. Let me know if it was helpful ?

Don’t forget to share it with your friends who are starting with Node.js or need to level up their Node.js skills.

References:

//nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original

//github.com/digitaldesignlabs/es6-promisify

You can read other articles like this at 101node.io.