Pepatah lama mengatakan bahwa Anda harus memilih alat yang tepat untuk pekerjaan itu. ES6 berjanji memberikan dasar-dasar. Jika semua yang Anda inginkan atau butuhkan adalah dasar-dasarnya, maka itu seharusnya bisa bekerja dengan baik untuk Anda. Tapi, ada lebih banyak alat di dalam bin alat daripada hanya dasar-dasarnya dan ada situasi di mana alat-alat tambahan sangat berguna. Dan, saya berpendapat bahwa janji ES6 bahkan kehilangan beberapa dasar-dasar seperti promisifikasi yang berguna di hampir setiap proyek node.js.
Saya paling akrab dengan perpustakaan janji Bluebird jadi saya akan berbicara sebagian besar dari pengalaman saya dengan perpustakaan itu.
Jadi, inilah 6 alasan utama saya untuk menggunakan perpustakaan Promise yang lebih mampu
Antarmuka async yang non-Promisified - .promisify()
dan .promisifyAll()
sangat berguna untuk menangani semua antarmuka async yang masih membutuhkan panggilan balik biasa dan belum mengembalikan janji - satu baris kode menciptakan versi yang dipatenkan dari seluruh antarmuka.
Lebih cepat - Bluebird secara signifikan lebih cepat daripada janji asli di sebagian besar lingkungan.
Mengurutkan pengulangan array async - Promise.mapSeries()
atau Promise.reduce()
memungkinkan Anda untuk beralih melalui sebuah array, memanggil operasi async pada setiap elemen, tetapi mengurutkan operasi async sehingga mereka terjadi satu demi satu, tidak semua pada waktu yang sama. Anda dapat melakukan ini karena server tujuan memerlukannya atau karena Anda harus meneruskan satu hasil ke yang berikutnya.
Polyfill - Jika Anda ingin menggunakan janji di versi klien browser yang lebih lama, Anda tetap akan memerlukan polyfill. Semoga juga mendapatkan polyfill yang mumpuni. Karena node.js memiliki janji ES6, Anda tidak perlu polyfill di node.js, tetapi Anda bisa menggunakan browser. Jika Anda mengkode server dan klien node.js, mungkin sangat berguna untuk memiliki perpustakaan janji dan fitur yang sama di keduanya (lebih mudah untuk berbagi kode, konteks beralih antar lingkungan, menggunakan teknik pengkodean umum untuk kode async, dll. .).
Fitur yang berguna lainnya - Bluebird memiliki Promise.map()
, Promise.some()
, Promise.any()
, Promise.filter()
, Promise.each()
dan Promise.props()
semua yang kadang-kadang berguna. Sementara operasi ini dapat dilakukan dengan janji-janji ES6 dan kode tambahan, Bluebird datang dengan operasi-operasi ini yang sudah pra-dibangun dan pra-diuji sehingga lebih mudah dan lebih sedikit kode untuk menggunakannya.
Peringatan bawaan dan Jejak Tumpukan Penuh - Bluebird memiliki sejumlah peringatan bawaan yang mengingatkan Anda akan masalah yang mungkin salah kode atau bug. Misalnya, jika Anda memanggil fungsi yang membuat janji baru di dalam .then()
handler tanpa mengembalikan janji itu (untuk menautkannya ke rantai janji saat ini), maka dalam kebanyakan kasus, itu adalah bug yang tidak disengaja dan Bluebird akan memberi Anda peringatan untuk itu efek. Peringatan Bluebird built-in lainnya dijelaskan di sini .
Berikut ini beberapa detail tentang berbagai topik ini:
Promisikan Semua
Dalam setiap proyek node.js, saya segera menggunakan Bluebird di mana-mana karena saya menggunakan .promisifyAll()
banyak pada modul node.js standar seperti fs
modul.
Node.js sendiri tidak menyediakan antarmuka janji untuk modul built-in yang melakukan Iyn seperti fs
modul. Jadi, jika Anda ingin menggunakan janji dengan antarmuka tersebut, Anda dibiarkan menggunakan kode tangan sebagai pembungkus janji untuk setiap fungsi modul yang Anda gunakan atau mendapatkan perpustakaan yang bisa melakukan itu untuk Anda atau tidak menggunakan janji.
Bluebird Promise.promisify()
dan Promise.promisifyAll()
menyediakan pembungkus otomatis node.js yang memanggil API async konvensi untuk mengembalikan janji. Ini sangat berguna dan menghemat waktu. Saya menggunakannya sepanjang waktu.
Berikut ini contoh cara kerjanya:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Alternatifnya adalah dengan membuat pembungkus janji Anda sendiri untuk setiap fs
API yang ingin Anda gunakan:
const fs = require('fs');
function readFileAsync(file, options) {
return new Promise(function(resolve, reject) {
fs.readFile(file, options, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Dan, Anda harus melakukan ini secara manual untuk setiap fungsi API yang ingin Anda gunakan. Ini jelas tidak masuk akal. Ini kode boilerplate. Anda mungkin juga mendapatkan utilitas yang berfungsi untuk Anda. Bluebird Promise.promisify()
dan Promise.promisifyAll()
utilitas seperti itu.
Fitur Berguna Lainnya
Berikut adalah beberapa fitur Bluebird yang menurut saya berguna (ada beberapa contoh kode di bawah ini tentang bagaimana ini dapat menyimpan kode atau mempercepat pengembangan):
Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()
Selain fungsinya yang bermanfaat, Promise.map()
juga mendukung opsi konkurensi yang memungkinkan Anda menentukan berapa banyak operasi yang boleh dijalankan pada saat yang sama yang sangat berguna ketika Anda memiliki banyak hal yang harus dilakukan, tetapi tidak dapat membanjiri beberapa di luar sumber.
Beberapa di antaranya dapat disebut stand-alone dan digunakan berdasarkan janji yang dengan sendirinya berubah menjadi iterable yang dapat menyimpan banyak kode.
Polyfill
Dalam proyek peramban, karena Anda umumnya ingin tetap mendukung beberapa peramban yang tidak memiliki dukungan Janji, Anda tetap memerlukan polyfill. Jika Anda juga menggunakan jQuery, kadang-kadang Anda bisa menggunakan janji dukungan yang dibangun ke dalam jQuery (meskipun sangat tidak standar dalam beberapa hal, mungkin diperbaiki di jQuery 3.0), tetapi jika proyek tersebut melibatkan aktivitas async yang signifikan, saya menemukan fitur-fitur yang diperluas di Bluebird sangat berguna.
Lebih cepat
Juga patut dicatat bahwa janji-janji Bluebird tampaknya jauh lebih cepat daripada janji-janji yang tertanam dalam V8. Lihat posting ini untuk diskusi lebih lanjut tentang topik itu.
A Big Thing Node.js is Missing
Apa yang akan membuat saya mempertimbangkan untuk menggunakan Bluebird lebih sedikit dalam pengembangan node.js adalah jika node.js dibangun dalam fungsi yang menjanjikan sehingga Anda dapat melakukan sesuatu seperti ini:
const fs = requirep('fs');
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Atau hanya menawarkan metode yang sudah dijanjikan sebagai bagian dari modul bawaan.
Sampai saat itu, saya melakukan ini dengan Bluebird:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Tampaknya agak aneh untuk memiliki dukungan ES6 janji yang dibangun ke node.js dan tidak memiliki modul built-in yang mengembalikan janji. Ini perlu disortir dalam node.js. Sampai saat itu, saya menggunakan Bluebird untuk menjanjikan seluruh perpustakaan. Jadi, rasanya seperti janji-janji sekitar 20% diimplementasikan di node.js sekarang karena tidak ada modul built-in yang memungkinkan Anda menggunakan janji-janji tanpa membungkusnya terlebih dahulu secara manual.
Contohnya
Berikut adalah contoh Promises vs Bluebird yang jelas dan Promise.map()
untuk membaca sekumpulan file secara paralel dan memberi tahu ketika dilakukan dengan semua data:
Janji Biasa
const files = ["file1.txt", "fileA.txt", "fileB.txt"];
const fs = require('fs');
// make promise version of fs.readFile()
function fsReadFileP(file, options) {
return new Promise(function(resolve, reject) {
fs.readFile(file, options, function(err, data) {
if (err) return reject(err);
resolve(data);
});
});
}
Promise.all(files.map(fsReadFileP)).then(function(results) {
// files data in results Array
}, function(err) {
// error here
});
Bluebird Promise.map()
danPromise.promisifyAll()
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const files = ["file1.txt", "fileA.txt", "fileB.txt"];
Promise.map(files, fs.readFileAsync).then(function(results) {
// files data in results Array
}, function(err) {
// error here
});
Berikut adalah contoh Promises vs Bluebird yang jelas dan Promise.map()
ketika membaca sekelompok URL dari host jarak jauh tempat Anda dapat membaca paling banyak 4 sekaligus, tetapi ingin menyimpan sebanyak mungkin permintaan secara paralel sesuai yang diizinkan:
Janji JS Biasa
const request = require('request');
const urls = [url1, url2, url3, url4, url5, ....];
// make promisified version of request.get()
function requestGetP(url) {
return new Promise(function(resolve, reject) {
request.get(url, function(err, data) {
if (err) return reject(err);
resolve(data);
});
});
}
function getURLs(urlArray, concurrentLimit) {
var numInFlight = 0;
var index = 0;
var results = new Array(urlArray.length);
return new Promise(function(resolve, reject) {
function next() {
// load more until concurrentLimit is reached or until we got to the last one
while (numInFlight < concurrentLimit && index < urlArray.length) {
(function(i) {
requestGetP(urlArray[index++]).then(function(data) {
--numInFlight;
results[i] = data;
next();
}, function(err) {
reject(err);
});
++numInFlight;
})(index);
}
// since we always call next() upon completion of a request, we can test here
// to see if there was nothing left to do or finish
if (numInFlight === 0 && index === urlArray.length) {
resolve(results);
}
}
next();
});
}
Janji Bluebird
const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'));
const urls = [url1, url2, url3, url4, url5, ....];
Promise.map(urls, request.getAsync, {concurrency: 4}).then(function(results) {
// urls fetched in order in results Array
}, function(err) {
// error here
});