欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

如何将 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化,可以借助工具实现这种目的。例如,BluebirdQ、原生的 util.promisify等。

相关资料