异步回调函数改造

946 阅读2分钟

异步回调函数改造

如果你不知道Promise和async await的基本用法,建议学习了之后再往下看。

宇宙节校招的一道题:

  1. node.js里写一个读取文件的函数
  2. callback改造成Promise或者async实现

回调函数版本

主要记住node的API大部分回调函数err在前面,data在后面就OK了。

const fs = require('fs');
​
function readFileCallBack() {
  fs.readFile('./test.txt', 'utf8', (err, data) => {
    if (err) {
      console.error(err);
      return;
    }
    console.log(data);
  });
}
​
readFileCallBack();
​

Promise版本

改成Promise实现也挺简单的,记住Promise的方法就行了。我当时还以为是要我使用Promise实现一个通用的函数,那比较困难,但是其实面试官不是这个意思,他是想让我继续改造上面👆🏻的功能,那就很方便写了。

function readFilePromise() {
  return new Promise((resolve, reject) => {
    fs.readFile('./test.txt', 'utf8', (err, data) => {
      if (err) {
        reject(err);
      }
      resolve(data);
    });
  });
}
readFilePromise()
  .then(
    (data) => console.log(data),
    (err) => console.error(err)
  )
  .catch((err) => console.error(err));

这里需要关注的一个概念就是then里的err处理和catch里的err处理,它们之间的关系是怎么样的。感兴趣的可以自己去跑一下,总结一句话就是写Promise一定不要忘了把catch写在最后,这很关键。

Async版本

然后,继续将这个改变为async await的形式。Promise虽然改变了callback的情况,将回调变成链式调用,但是Promise的缺点也很明显:首先是不太直观,然后是catch错误的问题,需要写很多重复的代码。

实现将Promise改造为async函数,实现下面这一步其实就完成了大部分:将改造的函数进行Promise化 。因为await后必须紧跟异步任务函数,其他同步的都不生效。此时,上面已经实现了,我只需要拿来用就行了。

async需要注意的是:注意错误处理 ,很多时候会忘记导致错误让程序中断,这是非常严重的失误。

async function readFileAsync() {
  try {
    const data = await readFilePromise();
    console.log(`data:${data}`);
  } catch (err) {
    console.error(err);
  }
}
readFileAsync();
​

因为fs其实自带Promise,我们可以不用使用之前实现的readFilePromise也能实现这个功能。

// 直接取promise化的fs用
const fs = require('fs').promises;
async function readFileAsync() {
  try {
    const data = await fs.readFile('./test.txt', 'utf8');
    console.log(`data:${data}`);
  } catch (err) {
    console.error(err);
  }
}
readFileAsync();
​

async函数返回的是Promise实例,也可以这样去处理错误,不过又绕到了Promise上了:

async function readFileAsync() {
    const data = await fs.readFile('./test1.txt', 'utf-8');
    return data;
}
​
readFileAsync()
  .then(data => console.log(`data:${data}`))
  .catch(err => console.error(`err:${err}`))
​

async的写法看起来就比较像同步函数了,但是async也有一些坑,比如不能写在循环条件里等等。

总结

在将callback转化为Promise或者async函数时,非常非常需要注意的点就是错误处理。如果异步的情况比较复杂,又忘记错误处理的话,测试是不一定能够完全覆盖里面的所有状态的,比较容易出错。

  • Promise里的catch err
  • async里的try catch