如何将 callback 接口 转化成 Async/Await
程序员文章站
2022-03-30 21:55:39
...
背景
今天在使用七牛 nodeJS SDK 做服务器端文件上传时,发现其封装的API,使用起来非常不方便。于是,想将callback API封装成自己习惯的 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
方法中,最后一个参数是一个回调函数,回调函数的第一个参数是一个 error
参数。这种风格的函数称为 Error-First Callback
。它有两个特征:
- 回调的第一个参数为一个错误对象
- 第二个参数保留给任何成功的数据
在NodeJS的世界中,我们经常会看到这种写法。但是,这种写法不是很好维护。我们需要在回调中写很多判断。除此之外,我们将异步的业务逻辑,放入了回调中。这样将导致主逻辑不是很清晰。
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
,我们都知道异步操作,在编程中是很难处理的。人们为了解决这个问题,尝试过callback、promise对象、Generator函数,但都不够彻底,使用起来仍不够简洁方便。
异步编程的最高境界,就是根本不用关心它是不是异步。
解决方法
我们将 formUploader.put
方法包一层promise,然后在主逻辑中,调用它。
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业务逻辑
// 将obj,转化为 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 函数
const { promisify } = require('util');
const fs = require('fs');
const statAsync = promisify(fs.stat);
statAsync('.').then(stats => {
console.log(stats);
}).catch((err) => {
console.log(err);
});
总结
实际上,在工作中不用大量手动将 nodeback
(Error-First Callback) Promise化,可以借助工具实现这种目的。例如,Bluebird
、Q
、原生的 util.promisify
等。