创建异步函数以稍后在快速路由中调用结果

时间:2019-06-17 08:17:30

标签: javascript express async-await

我必须通过API从Box.com获取一个Excel文件,然后将Excel转换为JSON。稍后,将使用Express和Handlebars呈现JSON。

我创建了以下函数来获取Excel / JSON:

let getExternalFile = async () => {
  const stream = await client.files.getReadStream('123456789');
  const external = await fs.createWriteStream('/external.xlsx');
  stream.pipe(external);
  let finished = await external.on('finish', async () => {
    const excel = await excelToJson({
      source: fs.readFileSync(external.path),
      header: {
        rows: 1
      },
      columnToKey: {
        A: "edate",
        B: "esource"
      },
      sheets: ['Sheet1']
    });
    console.log(excel);
    return excel
  })
}

我现在的问题是,执行此操作时,我不知道如何解决待处理的Promise:

let testData = getExternalFile();
console.log(testData); // Promise { <pending> }

我将使用这种功能下载多个文件,然后希望将其存储在变量中,以便稍后将其传递到我的快速路线。

app.get('/', (req, res) => {
    res.render('stories.ejs', {
        msg: "Welcome to stories.ejs", 
        odata: odata, 
        edata: edata,
        ...
    });
});

我必须将其包装在匿名异步函数中吗?

1 个答案:

答案 0 :(得分:2)

async / await并不是所有异步事物的灵丹妙药。它们被设计为仅与Promises一起使用。具体来说,async / await不适用于事件发射器。 getExternalFile的正确实现应为:

let getExternalFile = async () => {
  const stream = client.files.getReadStream('123456789'); // no await here
  const external = fs.createWriteStream('/external.xlsx'); // no await here
  stream.pipe(external);

  let finished = new Promise ((resolve, reject) => {
    external.on('finish', async () => { // cannot use await here
      const excel = await excelToJson({
        source: fs.readFileSync(external.path),
        header: {
          rows: 1
        },
        columnToKey: {
          A: "edate",
          B: "esource"
        },
        sheets: ['Sheet1']
      });
      console.log(excel);

      resolve(excel);
    })
  });

  return finished; // return the promise so you can await it later
}

所有非承诺函数(我不会将它们称为非“异步”函数,因为它将使人们混淆异步异步函数和非异步异步函数之间的区别,因为人们有时会使用“异步”来表示异步,有时甚至意思是“异步”关键字,它们是承诺生成函数)……我离题了……所有非承诺函数都不适用于await。这包括事件发射器x.on('some_event' ... )之类的东西。

在这种情况下,您需要将它们包装在new Promise()中,以将其转换为Promises。

现在,我们已经重写了上面的函数,我们可以简单地等待结果:

app.get('/', async /* <--IMPORTANT */ (req, res) => {
    let testData = await getExternalFile(); // use await here!!

    // ...
});

如果您有多个等待,则功能可能会变慢:

app.get('/', async /* <--IMPORTANT */ (req, res) => {
    let testData = await getExternalFile();
    let testData2 = await getExternalFile2(); // SLOW!

    // ...
});

如果您可以摆脱它,则可以并行执行异步功能:

app.get('/', async /* <--IMPORTANT */ (req, res) => {
    let testDataArray = await Promise.all([
        getExternalFile(), getExternalFile2()  // FAST!
    ]);

    // ...
});

Epologue:错误

在真实代码中,您应该在等待代码的地方捕获错误,以防止服务器崩溃:

app.get('/', async /* <--IMPORTANT */ (req, res) => {
    try {
        let testData = await getExternalFile(); // use await here!!

        // ...
    }
    catch (err) {

        // ...
    }
});

或者,您可以使用express-async-handler之类的中间件来捕获异步错误。