สุภาษิตโบราณไปว่าคุณควรเลือกเครื่องมือที่เหมาะสมสำหรับงาน ES6 สัญญาว่าจะให้ข้อมูลเบื้องต้น หากทุกสิ่งที่คุณต้องการหรือจำเป็นต้องมีคือพื้นฐานสิ่งนั้นก็ควรจะใช้ได้ดีสำหรับคุณ แต่มีเครื่องมือในถังขยะเครื่องมือมากกว่าพื้นฐานและมีสถานการณ์ที่เครื่องมือเพิ่มเติมเหล่านั้นมีประโยชน์มาก และฉันขอยืนยันว่าสัญญา ES6 นั้นยังขาดพื้นฐานบางอย่างเช่นการทำสัญญาที่มีประโยชน์ในทุกโครงการของ node.js
ฉันคุ้นเคยมากที่สุดกับห้องสมุดสัญญาของ Bluebirdดังนั้นฉันจะพูดส่วนใหญ่จากประสบการณ์ของฉันกับห้องสมุดนั้น
ดังนั้นนี่คือเหตุผล 6 อันดับแรกของฉันในการใช้ห้องสมุดสัญญาที่มีความสามารถมากกว่า
อินเตอร์เฟส async ที่ไม่ได้รับการสัญญา - .promisify()
และ.promisifyAll()
มีประโยชน์อย่างเหลือเชื่อในการจัดการอินเทอร์เฟซ async ทั้งหมดที่ยังคงต้องการการโทรกลับธรรมดาและยังไม่ส่งคืนสัญญา - โค้ดหนึ่งบรรทัดสร้างเวอร์ชันที่ได้รับการรับรองของอินเตอร์เฟสทั้งหมด
เร็วกว่า - Bluebird เร็วกว่าคำมั่นสัญญาดั้งเดิมในสภาพแวดล้อมส่วนใหญ่อย่างมีนัยสำคัญ
การเรียงลำดับของการทำซ้ำอาร์เรย์ของ async - Promise.mapSeries()
หรือPromise.reduce()
อนุญาตให้คุณวนซ้ำผ่านอาร์เรย์เรียกการดำเนินการ async ในแต่ละองค์ประกอบ แต่เรียงลำดับการดำเนินการของ async เพื่อให้เกิดการเรียงตัวกันทีละรายการไม่ใช่ทั้งหมดในเวลาเดียวกัน คุณสามารถทำสิ่งนี้ได้เนื่องจากเซิร์ฟเวอร์ปลายทางต้องการหรือเพราะคุณต้องส่งผลลัพธ์หนึ่งรายการต่อไป
Polyfill - หากคุณต้องการใช้สัญญาในเบราว์เซอร์ไคลเอนต์รุ่นเก่าคุณจะต้องใช้ polyfill อยู่ดี อาจได้รับ polyfill ที่มีความสามารถเช่นกัน เนื่องจาก node.js มีสัญญา ES6 คุณไม่จำเป็นต้องใช้ polyfill ใน node.js แต่คุณอาจใช้เบราว์เซอร์ หากคุณกำลังเข้ารหัสทั้งเซิร์ฟเวอร์ node.js และไคลเอนต์อาจเป็นประโยชน์อย่างมากที่จะมีไลบรารี่และฟีเจอร์ที่เหมือนกันทั้งคู่ (ง่ายต่อการแชร์รหัสสลับบริบทระหว่างสภาพแวดล้อมใช้เทคนิคการเข้ารหัสทั่วไปสำหรับรหัส async ฯลฯ .)
คุณสมบัติที่เป็นประโยชน์อื่น ๆ - ครามมีPromise.map()
, Promise.some()
, Promise.any()
, Promise.filter()
, Promise.each()
และPromise.props()
ทั้งหมดที่มีบางครั้งที่มีประโยชน์ ในขณะที่การดำเนินการเหล่านี้สามารถดำเนินการได้ด้วยสัญญา ES6 และรหัสเพิ่มเติม Bluebird มาพร้อมกับการดำเนินการเหล่านี้ที่สร้างไว้ล่วงหน้าและทดสอบล่วงหน้าแล้วดังนั้นจึงเป็นเรื่องง่ายและรหัสน้อยกว่าที่จะใช้
คำเตือนในตัวและการติดตามสแต็คเต็มรูปแบบ - Bluebird มีคำเตือนในตัวหลายตัวที่แจ้งเตือนคุณถึงปัญหาที่อาจเป็นรหัสผิดหรือข้อผิดพลาด ตัวอย่างเช่นหากคุณเรียกใช้ฟังก์ชันที่สร้างสัญญาใหม่ภายใน.then()
ตัวจัดการโดยไม่ส่งคืนสัญญานั้น (เพื่อเชื่อมโยงสัญญานั้นเข้ากับห่วงโซ่สัญญาปัจจุบัน) ในกรณีส่วนใหญ่นั่นคือข้อผิดพลาดโดยไม่ตั้งใจและ Bluebird จะเตือนคุณ ผล อื่น ๆ ในตัวคำเตือนครามมีการอธิบายไว้ที่นี่
นี่คือรายละเอียดเพิ่มเติมเกี่ยวกับหัวข้อต่าง ๆ เหล่านี้:
PromisifyAll
ในโปรเจ็กต์ node.js ใด ๆ ฉันก็ใช้ Bluebird ได้ทุกที่เพราะฉันใช้.promisifyAll()
บ่อยๆในโมดูล node.js มาตรฐานเช่นfs
โมดูล
Node.js ไม่ได้จัดเตรียมอินเทอร์เฟซแบบสัญญากับโมดูลในตัวที่ทำ async IO เหมือนfs
โมดูล ดังนั้นหากคุณต้องการใช้สัญญากับส่วนต่อประสานเหล่านั้นคุณจะเหลือรหัสมือเป็นตัวห่อสัญญารอบ ๆ แต่ละฟังก์ชั่นโมดูลที่คุณใช้หรือรับไลบรารีที่สามารถทำสิ่งนั้นเพื่อคุณหรือไม่ใช้สัญญา
Bluebird's Promise.promisify()
และPromise.promisifyAll()
จัดเตรียมการห่อ node.js ที่เรียกแบบแผน async APIs เพื่อคืนสัญญา มันมีประโยชน์มากและประหยัดเวลา ฉันจะใช้มันตลอดเวลา.
นี่คือตัวอย่างของวิธีการทำงาน:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
ทางเลือกอื่นคือสร้าง wrapper สัญญาของคุณเองสำหรับแต่ละfs
API ที่คุณต้องการใช้:
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
});
และคุณต้องทำสิ่งนี้ด้วยตนเองสำหรับแต่ละฟังก์ชั่น API ที่คุณต้องการใช้ ชัดเจนว่าไม่เข้าท่า มันเป็นรหัสสำเร็จรูป คุณอาจได้รับยูทิลิตี้ที่เหมาะกับคุณ ครามPromise.promisify()
และPromise.promisifyAll()
เป็นยูทิลิตี้ดังกล่าว
คุณสมบัติที่มีประโยชน์อื่น ๆ
ต่อไปนี้เป็นคุณสมบัติบางอย่างของ Bluebird ที่ฉันพบว่ามีประโยชน์โดยเฉพาะ (มีตัวอย่างรหัสคู่ด้านล่างเกี่ยวกับวิธีการบันทึกรหัสหรือการพัฒนาความเร็ว)
Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()
นอกเหนือจากฟังก์ชั่นที่มีประโยชน์แล้วPromise.map()
ยังรองรับตัวเลือกการทำงานพร้อมกันที่ช่วยให้คุณระบุจำนวนการดำเนินการที่ควรได้รับอนุญาตให้ทำงานในเวลาเดียวกันซึ่งมีประโยชน์อย่างยิ่งเมื่อคุณมีหลายอย่างที่ต้องทำ ทรัพยากร.
บางส่วนของสิ่งเหล่านี้สามารถเรียกได้ว่าเป็นแบบสแตนด์อะโลนและใช้กับสัญญาที่ตัวเองแก้ไขเป็น iterable ซึ่งสามารถบันทึกรหัสได้จำนวนมาก
polyfill
ในโครงการเบราว์เซอร์เนื่องจากโดยทั่วไปคุณต้องการสนับสนุนเบราว์เซอร์บางตัวที่ไม่มีการสนับสนุนตามสัญญาคุณจึงจำเป็นต้องมีโพลีฟิลอยู่แล้ว หากคุณใช้ jQuery ด้วยบางครั้งคุณสามารถใช้สัญญาสนับสนุนที่สร้างไว้ใน jQuery (แม้ว่าจะไม่ได้มาตรฐานอย่างเจ็บปวดในบางวิธีอาจแก้ไขใน jQuery 3.0) แต่ถ้าโครงการนั้นเกี่ยวข้องกับกิจกรรม async ที่มีนัยสำคัญฉันพบ คุณสมบัติเพิ่มเติมใน Bluebird มีประโยชน์มาก
ได้เร็วขึ้น
นอกจากนี้ยังควรสังเกตว่าสัญญาของ Bluebird นั้นเร็วกว่าสัญญาที่สร้างไว้ใน V8 อย่างมีนัยสำคัญ ดูโพสต์นี้สำหรับการสนทนาเพิ่มเติมในหัวข้อนั้น
Node สิ่งที่ยิ่งใหญ่สิ่งที่ขาดหายไป
สิ่งที่จะทำให้ฉันพิจารณาว่าใช้ Bluebird น้อยลงในการพัฒนา node.js หาก node.js สร้างขึ้นในฟังก์ชั่น promisify เพื่อให้คุณสามารถทำสิ่งนี้:
const fs = requirep('fs');
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
หรือเพียงแค่เสนอวิธีการที่ได้รับการเสนอให้เป็นส่วนหนึ่งของโมดูลในตัว
ก่อนหน้านี้ฉันทำกับ Bluebird:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
ดูเหมือนแปลกเล็กน้อยที่จะมีการสนับสนุนสัญญา ES6 ที่สร้างไว้ใน node.js และไม่มีโมดูลในตัวที่ส่งคืนสัญญา สิ่งนี้จำเป็นต้องแยกออกใน node.js ก่อนหน้านี้ฉันใช้ Bluebird เพื่อแนะนำห้องสมุดทั้งหมด ดังนั้นจึงรู้สึกว่ามีการใช้งานสัญญาประมาณ 20% ใน node.js ในขณะนี้เนื่องจากไม่มีโมดูลในตัวให้คุณใช้สัญญากับพวกเขาโดยไม่ต้องห่อหุ้มด้วยตนเองก่อน
ตัวอย่าง
นี่คือตัวอย่างของ Promises vs. Bluebird และPromise.map()
สำหรับการอ่านชุดของไฟล์ในแบบคู่ขนานและการแจ้งเตือนเมื่อทำกับข้อมูลทั้งหมด:
สัญญาธรรมดา
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
});
ครามPromise.map()
และPromise.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
});
นี่คือตัวอย่างของ Promises vs. Bluebird และPromise.map()
เมื่ออ่านกลุ่มของ URL จากรีโมตโฮสต์ที่คุณสามารถอ่านได้ครั้งละไม่เกิน 4 รายการ แต่ต้องการเก็บคำขอจำนวนมากในแบบคู่ขนานตามที่ได้รับอนุญาต:
สัญญา JS ธรรมดา
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();
});
}
สัญญาของ 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
});