Promisification Trong JavaScript
Tổng quan về Promisification
"Promisification
” là một thuật ngữ để chỉ sự chuyển đổi. Ở đây nó chỉ sự chuyển đổi từ function callback
sang một function trả lại một promise
. Cách convert như vậy thường được sử dụng nhiều trong thực tế, vì nhiều function và thư viện được xây dựng dựa trên callback. Tuy nhiên việc sử dụng promise
sẽ mang lại nhiều thuận tiện hơn.
Để hiểu rõ hơn, chúng ta hãy xem ví dụ, chúng ta có loadScript(src, callback)
. Từ các bài học trước, bạn có thể xem lại ở đây
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(script);
}
// usage:
// loadScript('path/script.js', (err, script) => {...})
Function này tải một script với giá trị đã cho src
, rồi gọi callback(err)
trong trường hợp có lỗi hoặc callback(null, script)
trong trường hợp thành công.
Bây giờ chúng ta sẽ tạo một hàm mới loadScriptPromise(src)
, chức năng này cũng hoạt động tương tự như function
ở trên, nhưng nó trả về một promises
.
let loadScriptPromise = function(src) {
return new Promise((resolve, reject) => {
loadScript(src, (err, script) => {
if (err) reject(err);
else resolve(script);
});
});
};
// usage:
// loadScriptPromise('path/script.js').then(...)
Bây giờ loadScriptPromise
phù hợp tốt với code trên promise
. Nếu chúng ta thích promise
hơn callback
thì chúng ta hoàn toàn có thể sử dụng nó để thay thế. Trong thực tế, chúng ta có thể cần promisify
hơn là một function, vì vậy sẽ hợp lý khi sử dụng nó. Chúng ta sẽ gọi nó là promisify(f)
: nó chấp nhận một promisify function
f
và trả về một wrapper function
function promisify(f) {
return function (...args) { // return a wrapper-function (*)
return new Promise((resolve, reject) => {
function callback(err, result) { // our custom callback for f (**)
if (err) {
reject(err);
} else {
resolve(result);
}
}
args.push(callback); // append our custom callback to the end of f arguments
f.call(this, ...args); // call the original function
});
};
}
// usage:
let loadScriptPromise = promisify(loadScript);
loadScriptPromise(...).then(...);
Ở đây, function
callback
trong function
promisify
ban đầu có hai đối số (err, result)
. Nhưng điều gì sẽ xảy ra nếu nó có một callback
với nhiều đối số hơn callback(err, res1, res2, ...)
? Vì vậy phiên bản nâng cao hơn hơn của nó ra đời promisify
.
- Khi function là
promisify(f)
, nó sẽ hoạt động tương tự như phiên bản cũ ở trên. - Khi function là
promisify(f, true)
, nó sẽ trả vềpromise
bằng mảng kết quảcallback
. Đó là chính xác là những gì chúng ta cần cho các callback với nhiều đối số.
// promisify(f, true) to get array of results
function promisify(f, manyArgs = false) {
return function (...args) {
return new Promise((resolve, reject) => {
function callback(err, ...results) { // our custom callback for f
if (err) {
reject(err);
} else {
// resolve with all callback results if manyArgs is specified
resolve(manyArgs ? results : results[0]);
}
}
args.push(callback);
f.call(this, ...args);
});
};
}
// usage:
f = promisify(f, true);
f(...).then(arrayOfResults => ..., err => ...);
Như bạn có thể thấy, về cơ bản nó giống như trên, nhưng resolve
được gọi với chỉ một hoặc tất cả các đối số tùy thuộc vào việc có đúng hay không manyArgs
. Để biết thêm các định dạng callback
, chúng ta có thể sử dụng promisify
mà không cần sử dụng các helper. Ngoài ra còn có các mô-đun có chức năng promisification
, ví dụ như es6-promisify . Trong Node.js, có util.promisify
tích hợp cho việc đó.
Tổng kết
Vậy là bài học ngày hôm nay của chúng ta đã khép lại. Để giúp các bạn thuật tiện hơn trong quá trình thực hành và tránh các sai sót chúng ta sẽ điểm lại một số lưu ý quan trọng ở đây. Hẹn gặp lại các bạn ở bài sau.
Promisification
là một cách tiếp cận tuyệt vời, đặc biệt là khi bạn sử dụngasync/await
, nhưng không phải là sự thay thế hoàn toàn chocallback
.- Hãy nhớ rằng, một
promise
có thể chỉ có một kết quả, nhưng về mặt kỹ thuật, mộtcallback
có thể gồm nhiều cáccallback
khác bên trong. - Vì vậy,
Promisification
chỉ dành cho các hàmcallback
không lồng nhau. Cáccallback
tiếp theo sẽ bị bỏ qua