简体   繁体   中英

NodeJS Express API call structure asynchronous events

I am doing a hobby project in NodeJS and Express, but I am overwhelmed by dealing with asynchronous calls. I have encountered a bug that I would like to bounce with the community.

I currently have this layout using Express to which I am sending post requests to.

Express (backend)

app.post("/", async (req, res) => {
    const createDocument = (args1) => {

        // declare some variables ...
        // for loop ...
        // (quite fast)

        loadFile(args2, async () => {

            // upload a file (quite slow)
            await upload(file, filePath)

            //send post request to third party (quite fast)
            let res = await axios.post(url, data, {
                headers: {
                    'X-Auth-Token': KEY
                }
            }).then(res => console.log('success')).catch(error => console.log('failure'))
        })
    }

    const args1 = "someargs"

    await new Promise(resolve => setTimeout(resolve, 2000))
    await createDocument(args1)

    res.end()
})

React (frontend)

const saveDocButtonClicked = useCallback(async (event) => {

    // do some stuff ...

    await axios.post('/', {
        headers: {
            'Content-Type': 'application/json'
        },
        jsonData
    }).then(res => console.log('Data sent')).catch(err => console.log(err.data))

    // do some other stuff ...

}

I want the React app to wait, and not proceed before getting a proper response from the API call to the Express backend.

And I want the Express app to give a proper response only when it has performed all of its tasks.

This has worked fine so far for smaller files, but as they increase in size, they no longer get saved / stored in the database.

The key difference I noticed is that in these bad cases, the strings success or failure which should come from this piece of code in the Express backend; .then(res => console.log('success')).catch(error => console.log('failure')) no longer gets outputted. So somewhere the asynchronous events are probably jumping the gun.

I am sure that I have made tons of mistakes, please feel free to point them out, I would love to learn this, I am such a freshman on asynchronous calls.

Returning promise from createDocument will allow the await createDocument(args1) to wait until it completed execution

app.post("/", async (req, res) => {
const createDocument = (args1) => {
    return new Promise((resolve,reject)=>{
        // declare some variables ...
        // for loop ...
        // (quite fast)

        loadFile(args2, async () => {

            // upload a file (quite slow)
            await upload(file, filePath)

            //send post request to third party (quite fast)
            let res = await axios.post(url, data, {
                headers: {
                    'X-Auth-Token': KEY
                }
            }).then(res => console.log('success')).catch(error => console.log('failure'))
            resolve(res);
            //TODO:: Reject if some error occurs;
        })
    });
}

const args1 = "someargs"

await new Promise(resolve => setTimeout(resolve, 2000))
await createDocument(args1)

res.end()
})

Using async-await and with then/catch is the bad way to go for this kind of thing. With await you can do the job

try {
  const response = await axios.post('/', {
      headers: {
        'Content-Type': 'application/json'
      },
      jsonData
  })
  // Here is your data after response completed
  const data  = response.json()
} catch(err) {
  console.log(err)
} 

Or you can remove async/await and simply use then/catch

Your code seems syntactically correct (though it can be better by following some async/await best practices - but those should not affect the logic of your app). Can you provide some sample input, output, and your expected outputs so I can understand the question better ?

In the mean time, I think that it can just be upload() function is slow, especially for bigger files, so you just need to wait more before "success" or "failure" is logged

Edit: I figured something with the callback flow that did not go with the async/await flow. Seems like the createDocument did not wait, or know to wait for loadFile to finish, because loadFile stuff is "hidden" behind a callback. So I altered createDocument to return a Promise:

app.post("/", async (req, res) => {
    const createDocument = (args1) => {
        // Return a promise that waits for loadFile
        return new Promise((resolve, reject) => {
            // declare some variables ...
            // for loop ...
            // (quite fast)

            loadFile(args, () => {
                // I dont like using async with callback, so I'mma use .then()
                function makeAxiosRequest() {
                    return axios.post(url, data, {
                        headers: {
                            "X-Auth-Token": KEY,
                        },
                    });
                }
                // Only resolve createDocument after loadFile's stuff is finished
                upload(file, filePath)
                    .then(() => makeAxiosRequest())
                    .then(() => console.log("Success"))
                    .catch((err) => console.log("Failure"))
                    .finally(resolve());
            });
        });
    };

    const args1 = "someargs";

    await new Promise((resolve) => setTimeout(resolve, 2000));

    await createDocument(args1);

    // I like sending something to frontend for better debug experience
    return res.json({ message: "Respond now ends" });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM