Cara menggunakan janji di javascript

JavaScript (JS) terkenal dengan gaya asinkronnya dibandingkan dengan bahasa lain yang awalnya diakomodasi menggunakan callback. Terutama pada node. js yang hampir semua API inti adalah async. Namun callback itu sendiri terkadang kurang nyaman digunakan karena bisa menyebabkan callback hell, sulit untuk melakukan error handling, dll

Contoh callback sih, misalkan ada 4 file, A. txt, B. txt, C. txt, D. txt di direktori yang sama dan ingin membuka 4 file secara berurutan dengan callback

const fs = require('fs');fs.readFile('A.txt', 'utf8', (errA, resA) => {
fs.readFile('B.txt', 'utf8', (errB, resB) => {
fs.readFile('C.txt', 'utf8', (errC, resC) => {
fs.readFile('D.txt', 'utf8', (errD, resD) => {
console.log(resA, resB, resC, resD);
});
});
});
});

Seiring dengan berkembangnya komunitas, fitur, dan standar dari JS itu sendiri, banyak library yang mencoba menyederhanakan proses async dengan membuat API yang lebih sederhana dan atau lebih “rich” (kaya fitur), seperti library async, ketika, dan masih banyak lagi. Salah satu pendekatan paling sederhana adalah dengan menggunakan Janji, hingga beberapa proposal Janji diajukan sebagai standar di CommonJS (Janji A, B, KISS, D) dan pada akhirnya spesifikasi Janji A+ digunakan oleh standar (ES6). Tidak diragukan lagi, JS adalah bahasa tercepat untuk berubah setidaknya sampai tulisan ini dibuat

Promise API cenderung lebih mudah dan nyaman digunakan, asalkan kita memahami konsep scope dan async di JS. Berikut ini akan dibahas beberapa contoh penggunaan Promises

Membuat Janji Obyek

Sebelum mencoba hal berikut, pastikan Promise tersedia di lingkungan global. Simpul pengguna. js dapat langsung menggunakan Janji sejak versi stabil 0. 12. x, sedangkan untuk sisi client (browser) bisa merujuk ke. Jika env yang digunakan tidak support Promise, kita bisa menggunakan polyfill atau library tambahan lainnya seperti bluebird

const fs = require('fs');// contoh promise untuk async
const promiseAsync = new Promise((resolve, reject) => {
fs.readFile('A.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
promiseAsync
.then(res => console.log('hasil async: ', res))
.catch(err => console.log('error', err));
// contoh promise untuk sync
const promiseSync = new Promise(resolve => {
resolve('hasil');
});
promiseSync
.then(res => console.log('hasil sync: ', res));
_

Promise Konstruktor menerima satu argumen dalam bentuk fungsi yang menerima dua argumen yang biasa disebut penyelesaian dan penolakan. Semua argumen adalah fungsi layak panggilan balik. Proses yang terjadi dalam lingkup Promise dapat berupa asinkron atau sinkronisasi. Sehingga Promise sering digunakan sebagai standarisasi API, sehingga tidak perlu mengubah API saat mengubah dari proses sync ke async. Perlu diingat, eksekusi proses yang terjadi pada promise dilakukan langsung pada saat promise dibuat

Memanfaatkan API “lalu” dan “tangkap”.

Objek Promise memiliki metode/fungsi then dan catch, yang masing-masing memiliki fungsi untuk memproses hasil keberhasilan dan kegagalan

const fs = require('fs');const promiseA = new Promise((resolve, reject) => {
fs.readFile('A.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
const promiseB = new Promise((resolve, reject) => {
fs.readFile('B.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
const promiseFail = new Promise((resolve, reject) => {
fs.readFile('XXX.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
promiseA
.then(resA => {
console.log('hasil A: ', resA);
return promiseB;
})
.then(resB => {
console.log('hasil B: ', resB);
return promiseFail;
})
.then(resFail => {
console.log('hasil fail: ', resFail);
})
.catch(err => console.log('error: ', err));

Dalam contoh di atas log "hasil A. ” dan “hasil dari B. ” akan muncul tetapi “File results. " tidak, karena promiseFail sengaja dibuat error yaitu membaca file yang tidak ada, maka akan muncul log "error" yang meng-capture hasil error dari promiseFail

Pemrosesan Banyak Janji

Setelah memahami contoh dasar, selanjutnya akan dibahas contoh chaining yang memproses data dari lebih dari satu promise

Asumsikan pada 3 janji yang kita miliki, kita ingin membaca semua file, pada akhirnya semua hasil dirilis terlepas dari berhasil atau gagalnya

// variable outside promise scope
let resultA = null;
let resultB = null;
let resultFail = null;
promiseA
.then(resA => {
resultA = resA;
return promiseB;
})
.then(resB => {
resultB = resB;
return promiseFail;
})
.then(resFail => {
// remember this never happend
resultFail = resFail;
})
.catch(err => console.log('no worries, go ahead'))
.then(() => {
// here we have resultA, resultB, but resultFail still null;
console.log('resA: ', resultA);
console.log('resB: ', resultB);
console.log('resFail: ', resultFail);
});
// remember, resultA, B, and Fail are null here

Variabel dideklarasikan di luar lingkup janji. Selama janji belum selesai, variabel akan tetap nol, termasuk di baris terakhir (di luar cakupan janji). Perhatikan bahwa "catch" adalah sebelum yang terakhir "then", maksudnya adalah, pertama-tama kita mengatur semua kesalahan yang terjadi dari janji-janji yang telah dieksekusi, dan pada bagian terakhir, kita harus bisa mengantisipasi jika salah satu atau lebih hasilnya masih nihil karena proses async gagal. Penanganan kesalahan semacam ini jauh lebih nyaman dan mudah diterapkan daripada panggilan balik

Skenario kedua, asumsikan kita membutuhkan hasil dari semua janji, tetapi jika ada kesalahan di salah satunya, hasilnya adalah string "kesalahan", bukan nol

// Id error, write string 'error' instead of null
let resultA2 = null;
let resultB2 = null;
let resultFail2 = null;
promiseA
.then(resA => {
// will enter here
resultA2 = resA;
})
.catch(errA => {
// will be skipped
resultA2 = 'error';
})
.then(() => {
// always enter here
return promiseB;
})
.then(resB => {
// will enter here
resultB2 = resB;
})
.catch(errB => {
// will be skipped
resultB2 = 'error';
})
.then(() => {
// always enter here
return promiseFail;
})
.then(resFail => {
// will enter here
resultFail2 = resFail;
})
.catch(errFail => {
// will enter here
resultFail2 = 'error';
})
.then(() => {
console.log('resA: ', resultA2);
console.log('resB: ', resultB2);
console.log('resFail: ', resultFail2);
});
_

Salah satu kemudahan menggunakan promise adalah penanganan error yang lebih rapi dan dapat diprediksi. Pada contoh di atas, pengkodean dapat dipersingkat dan disederhanakan menjadi sebagai berikut (tanpa mengurangi fungsionalitas sedikitpun)

// Id error, write string 'error' instead of null
let resultA2 = null;
let resultB2 = null;
let resultFail2 = null;
promiseA
.then(resA => (resultA2 = resA))
.catch(errA => (resultA2 = 'error'))
.then(() => promiseB)
.then(resB => (resultB2 = resB))
.catch(errB => (resultB2 = 'error'))
.then(() => promiseFail)
.then(resFail => (resultFail2 = resFail))
.catch(errFail => (resultFail2 = 'error'))
.then(() => {
console.log('resA: ', resultA2);
console.log('resB: ', resultB2);
console.log('resFail: ', resultFail2);
});

Lekukan dapat disesuaikan dengan kebutuhan. Pada contoh penulisan di atas, akan sangat mudah untuk melakukan debug, yang janji akan menangani kesalahan, serta urutan prosesnya

Memanfaatkan Promise API. semua

Beberapa janji dapat dijalankan secara berurutan seperti contoh sebelumnya tetapi juga dapat dijalankan secara paralel menggunakan Promise API. semua

'use strict';const fs = require('fs');const promiseA = new Promise((resolve, reject) => {
fs.readFile('A.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
const promiseB = new Promise((resolve, reject) => {
fs.readFile('B.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
const promiseC = new Promise((resolve, reject) => {
fs.readFile('C.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
const promiseD = new Promise((resolve, reject) => {
fs.readFile('D.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
const promiseFail = new Promise((resolve, reject) => {
fs.readFile('XXX.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
// run success only
Promise.all([promiseA, promiseB, promiseC, promiseD])
.then(ress => {
console.log('result A: ', ress[0]);
console.log('result B: ', ress[1]);
console.log('result C: ', ress[2]);
console.log('result D: ', ress[3]);
})
.catch(err => console.log(err));
// run all
Promise.all([promiseA, promiseB, promiseC, promiseD, promiseFail])
.then(ress => {
console.log('result A: ', ress[0]);
console.log('result B: ', ress[1]);
console.log('result C: ', ress[2]);
console.log('result D: ', ress[3]);
console.log('result E: ', ress[4]);
})
.catch(err => console.log(err));
_

Beberapa ciri yang perlu diperhatikan

  1. Ketika hanya ada satu janji yang gagal, semua janji dianggap gagal
  2. Urutan hasil dari promise sama dengan urutan saat memanggil API semua
  3. Semua janji dijalankan secara bersamaan async

Jadi, Janji. all kurang cocok untuk mengeksekusi promise yang membutuhkan penanganan error atau akurasi pada setiap promise yang ada

Tapi kita bisa melakukan kesalahan penyerahan janji yang dijalankan pada Janji. semua, sehingga tidak ada kesalahan yang tertangkap. Contohnya adalah sebagai berikut

'use strict';const fs = require('fs');const promiseA = new Promise((resolve, reject) => {
fs.readFile('A.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
})
.catch(err => console.log('no worries, already handled'));
const promiseB = new Promise((resolve, reject) => {
fs.readFile('B.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
})
.catch(err => console.log('no worries, already handled'));
const promiseC = new Promise((resolve, reject) => {
fs.readFile('C.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
})
.catch(err => console.log('no worries, already handled'));
const promiseD = new Promise((resolve, reject) => {
fs.readFile('D.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
})
.catch(err => console.log('no worries, already handled'));
const promiseFail = new Promise((resolve, reject) => {
fs.readFile('XXX.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
})
.catch(err => console.log('no worries, already handled'));
// run all
Promise.all([promiseA, promiseB, promiseC, promiseD, promiseFail])
.then(ress => {
console.log('result A: ', ress[0]);
console.log('result B: ', ress[1]);
console.log('result C: ', ress[2]);
console.log('result D: ', ress[3]);
console.log('result E: ', ress[4]);
})
.catch(err => console.log(err));

Pada contoh diatas, “result E” akan berisi undefined, karena pada saat melakukan proses penanganan error, kita tidak mengembalikan apapun

Chainning dan Penanganan Kesalahan pada Janji

Pada contoh sebelumnya, sedikit dibahas masalah error handling. Berikut adalah contoh implementasi penanganan error dengan beberapa pendekatan. Selain penanganan error, ada juga contoh penggunaan variasi chaining pada API then dan catch

'use strict';const fs = require('fs');const promise = new Promise((resolve, reject) => {
fs.readFile('A.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
const promiseB = new Promise((resolve, reject) => {
fs.readFile('B.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
const promiseFail = new Promise((resolve, reject) => {
fs.readFile('XXX.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
})
// 1. editing success result with sync process
promise
.then(res => {
const newRes = res + 'additional string';
return newRes;
})
.then(res => console.log(res))
_

Contoh di atas menunjukkan bahwa pengembalian dalam janji akan dilanjutkan ke rantai berikutnya dan pengembalian dapat mengubah proses sinkronisasi

// 2. editing success result with async process
// using external variable
let result = null;
promise
.then(res => (result = res))
.then(() => promiseB)
.then(res => (result = result + res))
.then(res => console.log(res));
_

Dalam contoh di atas, proses pengembalian janji bisa menjadi janji lain. Tapi ingat, jika pengembaliannya berupa janji, untuk mendapatkan hasilnya perlu dieksekusi setelah "kemudian" (menunggu hingga proses selesai hingga "aman" untuk sinkronisasi dan async)

const fs = require('fs');// contoh promise untuk async
const promiseAsync = new Promise((resolve, reject) => {
fs.readFile('A.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
promiseAsync
.then(res => console.log('hasil async: ', res))
.catch(err => console.log('error', err));
// contoh promise untuk sync
const promiseSync = new Promise(resolve => {
resolve('hasil');
});
promiseSync
.then(res => console.log('hasil sync: ', res));
_0

Pada contoh di atas, promiseB dimodifikasi terlebih dahulu dengan hasil promise (A) secara internal. Sehingga tidak diperlukan variabel eksternal

const fs = require('fs');// contoh promise untuk async
const promiseAsync = new Promise((resolve, reject) => {
fs.readFile('A.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
promiseAsync
.then(res => console.log('hasil async: ', res))
.catch(err => console.log('error', err));
// contoh promise untuk sync
const promiseSync = new Promise(resolve => {
resolve('hasil');
});
promiseSync
.then(res => console.log('hasil sync: ', res));
_1

Pada contoh di atas penanganan error dilakukan secara internal pada sebuah proses promise besar yaitu promise A sebagai awal proses

Demikian beberapa ciri dari chaining promise(s) antara lain

  1. Pengembalian dalam blok janji akan diperluas ke blok "maka" berikutnya
  2. Ketika terjadi error (throw error) pada sebuah blok, error tersebut akan "ditangkap" oleh blok "catch" terdekat setelah blok error tersebut. Sehingga penanganan error dapat dilakukan untuk beberapa proses promise
  3. Pengembalian bisa dalam bentuk sinkronisasi yang diproses atau janji lainnya
  4. Ketika pembalikan itu berupa janji, makan "maka" setelah itu adalah hasil dari janji yang dibalik
  5. Jika ingin mengembalikan nilai dari promise awal dan promise berantai, maka proses modifikasi dilakukan di blok promise yang akan dibalik (contoh 3)
  6. Hasil error pada “catch” dapat ditangani dan dikembalikan dengan nilai hingga menjadi success (bukan error), sehingga dapat dilanjutkan ke blok janji “then” setelahnya

Buat Janji Objek Instan

Terkadang kita membutuhkan sebuah nilai yang saat ini prosesnya bisa dilakukan secara sinkron, namun kedepannya ada kemungkinan proses tersebut akan dilakukan secara async, maka standarisasi dengan promise sangat cocok untuk kasus tersebut. Namun, menghasilkan janji objek menggunakan "Janji baru..." dalam proses sinkronisasi tidaklah efisien. Dalam contoh berikutnya, dibahas cara membuat objek promise menggunakan Promise API. menyelesaikan dan berjanji. menolak

const fs = require('fs');// contoh promise untuk async
const promiseAsync = new Promise((resolve, reject) => {
fs.readFile('A.txt', 'utf8', (err, res) => {
if (err) reject(err);
resolve(res);
});
});
promiseAsync
.then(res => console.log('hasil async: ', res))
.catch(err => console.log('error', err));
// contoh promise untuk sync
const promiseSync = new Promise(resolve => {
resolve('hasil');
});
promiseSync
.then(res => console.log('hasil sync: ', res));
_2

Dalam contoh di atas "Janji. resolve()" di awal dilakukan hanya untuk memberikan ruang lingkup awal tanpa hasil apa pun, sehingga proses janji selanjutnya dapat ditulis dengan rapi

Janji. penyelesaian adalah objek instan yang dapat diteruskan ke "kemudian" saat Janji. reject adalah objek instan yang diteruskan ke "catch". Dalam hal API tidak ada ketentuan berapa nilai yang harus dikembalikan oleh Promise. menolak, tetapi ada baiknya kita mengikuti standar error di JS, yaitu return error adalah error objek yang dibuat dengan "New Error" (atau turunannya)

Kesimpulan

Janji dapat membantu "menjinakkan" proses asinkron di JS. Terlebih lagi, promise sudah menjadi standar di ECMA 2015 (ES6), sehingga developer bisa lebih nyaman menggunakan fitur dari promise untuk kebutuhan produksi. Walaupun masih ada kemungkinan spesifikasi standar akan berubah, mengingat bahasa JS berubah dengan sangat cepat

Seluruh kode sumber dari artikel ini dapat diakses secara terbuka di repositori https. //github. com/hallowin/blog-promise

Apa itu Janji dalam javascript?

Janji adalah salah satu fitur penting ES6 . Objek janji mewakili solusi atau kesalahan pada operasi Asinkron.

Kapan kita menggunakan asinkron?

Proses asinkron sering digunakan untuk komunikasi data , karena data adalah bagian inti dari aplikasi, konsep asinkron sangat penting untuk memahami.

Apa itu async Tunggu Javascript?

Async/await adalah fitur yang hadir sejak ES2017. Fitur ini memudahkan kita menangani proses asinkron . Async/Await adalah sintaks khusus yang digunakan untuk menangani Janji sehingga penulisan kode lebih efisien dan rapi.

Apa itu fungsi callback javascript?

Callback dalam Javascript adalah fungsi yang dikirim sebagai parameter ke fungsi lain. Fungsi di atas adalah contoh callback yang berjalan sinkron karena callback fungsi langsung dieksekusi pada proses fungsional yang bersifat sinkron.