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?
- Dengan panggilan balik, jika anda ingin melakukan sesuatu secara berurutan, anda harus menentukan
err
argumen dalam setiap panggilan balik, yang berlebihan. Dalam janji atau async-tunggu, anda hanya boleh menambahkan.catch
kaedah atau blok yang akan menangkap kesilapan yang berlaku di rantai janji - Dengan panggilan balik, anda tidak dapat mengawal kapan ia dipanggil, dalam konteks apa, atau berapa kali ia dipanggil, yang boleh menyebabkan kebocoran memori.
- 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:
- Bungkus fungsi dengan fungsi lain yang mengembalikan janji. Ia kemudian menyelesaikan atau menolak berdasarkan hujah panggilan balik.
- Promisification - Kami membuat fungsi util / helper
promisify
yang 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 thenable
janji.
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, getSumPromise
menyerahkan 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 getSumAsync
menjadi getSumPromise
yang mempunyai .then
dan .catch
kaedah
Mari tulis fungsi janji kami sendiri:
Sekiranya anda melihat langkah 1 dalam kod di atas, promisify
fungsi 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. args
akan 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 reject
janji.
Sebenarnya, itu akan ditentukan oleh getSumAsync
pelaksanaan fungsi asal - ia akan memanggil fungsi panggilan balik asal dan kita hanya perlu menentukannya. Kemudian berdasarkan err
dan result
kita akan reject
atau resolve
janji.
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 getSumAsync
akan 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/reject
mengikutinya.
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.