背景
今天在使用七牛 nodeJS SDK 做服務(wù)器端文件上傳時(shí),發(fā)現(xiàn)其封裝的API,使用起來非常不方便。于是,想將callback API封裝成自己習(xí)慣的 Async/Await的語法。
問題
我們先看一下七牛代碼
var formUploader = new qiniu.form_up.FormUploader(config);
var putExtra = new qiniu.form_up.PutExtra();
var key='test.txt';
formUploader.put(uploadToken, key, "hello world", putExtra, function(respErr,
respBody, respInfo) {
if (respErr) {
throw respErr;
}
if (respInfo.statusCode == 200) {
console.log(respBody);
} else {
console.log(respInfo.statusCode);
console.log(respBody);
}
});
在 formUploader.put方法中,最后一個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),回調(diào)函數(shù)的第一個(gè)參數(shù)是一個(gè) error 參數(shù)。這種風(fēng)格的函數(shù)稱為 Error-First Callback。它有兩個(gè)特征:
- 回調(diào)的第一個(gè)參數(shù)為一個(gè)錯(cuò)誤對(duì)象
- 第二個(gè)參數(shù)保留給任何成功的數(shù)據(jù)
在NodeJS的世界中,我們經(jīng)常會(huì)看到這種寫法。但是,這種寫法不是很好維護(hù)。我們需要在回調(diào)中寫很多判斷。除此之外,我們將異步的業(yè)務(wù)邏輯,放入了回調(diào)中。這樣將導(dǎo)致主邏輯不是很清晰。
Node style callbacks (nodebacks) have a particular format where the callbacks is always the last argument and its first parameter is an error.
那么,如何改造這樣的寫法呢?我們想到了 Async/Await,我們都知道異步操作,在編程中是很難處理的。人們?yōu)榱私鉀Q這個(gè)問題,嘗試過callback、promise對(duì)象、Generator函數(shù),但都不夠徹底,使用起來仍不夠簡(jiǎn)潔方便。
異步編程的最高境界,就是根本不用關(guān)心它是不是異步。
解決方法
我們將 formUploader.put方法包一層promise,然后在主邏輯中,調(diào)用它。
const stream_uploader = (token, key, readableStream) => {
return new Promise((resolve, reject) => {
formUploader.putStream(token, key, readableStream, putExtra, (res_error, res_body, res_info) => {
if (res_error) {
reject(res_error);
}
if (res_info.statusCode == 200) {
reslove(res_body);
} else {
reject({
code: res_info.statusCode,
body: res_body,
});
}
});
});
}
const main = async () => {
// ... 獲取token、key業(yè)務(wù)邏輯
// 將obj,轉(zhuǎn)化為 readableStream 流
const readableStream = await intoStream(JSON.stringify(obj));
try {
const result = await stream_uploader(token, key, readableStream, putExtra);
} catch (error) {
// 處理異常
}
};
main();
使用 util.promisify Promise化 nodebacks 函數(shù)
const { promisify } = require('util');
const fs = require('fs');
const statAsync = promisify(fs.stat);
statAsync('.').then(stats => {
console.log(stats);
}).catch((err) => {
console.log(err);
});
總結(jié)
實(shí)際上,在工作中不用大量手動(dòng)將 nodeback(Error-First Callback) Promise化,可以借助工具實(shí)現(xiàn)這種目的。例如,Bluebird、Q、原生的 util.promisify等。