javascriptnode.jsasynchronousvercel-micro

How to use nested asychronous code correctly?


I've created a little api using micro.

Running localhost:3000/get-status should return the data object. So far the console.log() is printing the expected object.

But on the browser I get Endpoint not found and on the server I get the error Si7021 reset failed: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

What am I doing wrong with my getStatus() function? I guess I've mixed up something with the promises and asynchronous things. And maybe I've nested the functions unnecessarily...

const { send } = require('micro')
const { router, get } = require('microrouter')
const Si7021 = require('si7021-sensor')

const getStatus = async (req, res) => {
  const si7021 = new Si7021({ i2cBusNo: 1 })
  const readSensorData = async () => {
    const data = await si7021.readSensorData()
    console.log(data)
    send(res, 201, { data })
  }

  si7021.reset()
    .then((result) => readSensorData())
    .catch((err) => console.error(`Si7021 reset failed: ${err} `))
}

const notFound = (req, res) => {
  console.log('NOT FOUND.')
  send(res, 404, 'Endpoint not found')
}

module.exports = router(
  get('/get-status', getStatus),
  get('/*', notFound)
)

Solution

  • Looks like your handler returns a promise that resolves immediately. Can you try to rewrite the last line like this?

    return si7021.reset()
        .then((result) => readSensorData())
        .catch((err) => console.error(`Si7021 reset failed: ${err}`));
    

    It could be bit more cleanly written as:

    const getStatus = async (req, res) => {
      try {
        const si7021 = new Si7021({ i2cBusNo: 1 });
        await si7021.reset();
        const data = await si7021.readSensorData();
        console.log(data);
        return send(res, 201, { data });
      } catch (e) {
        console.error(`Si7021 reset failed: ${err}`)
      }
    }
    

    But you'd probably want to send something in the catch handler as well.

    Also, mind you that the promise returned from

    si7021.reset()
        .then((result) => readSensorData());
    

    Doesn't only reject when the .reset fails. It also rejects readSensorData fails. So your error message is not complete. All in all I'd rather recommend something along the lines of:

    const getStatus = async (req, res) => {
      try {
        const si7021 = new Si7021({ i2cBusNo: 1 });
        await si7021.reset();
        const data = await si7021.readSensorData();
        console.log(data);
        send(res, 201, { data });
      } catch (e) {
        console.error(`Si7021 failed: ${err.message}`);
        send(res, 500, err.message);
      }
    }