NodeJS Express API 调用结构异步事件

Posted

技术标签:

【中文标题】NodeJS Express API 调用结构异步事件【英文标题】:NodeJS Express API call structure asynchronous events 【发布时间】:2021-09-10 03:35:53 【问题描述】:

我正在用 NodeJS 和 Express 做一个爱好项目,但是处理异步调用让我不知所措。我遇到了一个我想在社区中反弹的错误。

我目前使用 Express 进行此布局,我将向其发送帖子请求。

快递(后端)

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()
)

反应(前端)

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 ...


我希望 React 应用等待,在从 API 调用到 Express 后端得到正确响应之前不要继续。

我希望 Express 应用仅在完成所有任务后才能给出正确的响应。

到目前为止,这对于较小的文件效果很好,但随着文件大小的增加,它们不再被保存/存储在数据库中。

我注意到的关键区别在于,在这些糟糕的情况下,字符串 successfailure 应该来自 Express 后端的这段代码; .then(res => console.log('success')).catch(error => console.log('failure')) 不再被输出。所以在某个地方,异步事件可能会抢先一步。

我确信我犯了很多错误,请随时指出,我很想学习这个,我是异步调用的大一新生。

【问题讨论】:

我认为问题在于createDocument,您应该从该函数返回承诺,只要loadFile 完成,该函数就会解决。希望有帮助 好的,这在代码中会是什么样子?我有几个想法,但不能写在cmets小窗口里。 【参考方案1】:

您的代码在语法上似乎是正确的(尽管遵循一些 async/await 最佳实践可能会更好 - 但这些不应影响您的应用程序的逻辑)。您能否提供一些示例输入、输出和您的预期输出,以便我更好地理解问题?

同时,我认为可能只是upload() 功能很慢,尤其是对于较大的文件,所以您只需要等待更多时间才能记录“成功”或“失败”

编辑:我想出了一些与异步/等待流程不同的回调流程。似乎 createDocument 没有等待,或者知道等待 loadFile 完成,因为 loadFile 的东西被“隐藏”在回调后面。所以我改变了 createDocument 来返回一个 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" );
);

【讨论】:

嘿,我可以使用一些示例输入和输出对原始问题进行编辑。然而奇怪的是,“成功”或“失败”从未被输出,即使在几个小时之后。 我认为您定义 loadFile 函数的方式可能有问题。它看起来既异步又同时使用回调。如果您进行编辑,您能否包含一些有关如何定义 loadFile 函数的详细信息? 该函数实际上不是我定义的,而是我正在使用的包的内置函数,本质上我是用一个长的base64字符串输入它,当它完全加载时,它会触发回调。 我想出了一些与异步/等待流程不同的回调流程。似乎 createDocument 没有等待,或者知道等待 loadFile 完成,因为 loadFile 的东西被“隐藏”在回调后面。你能检查我的编辑看看它是否有效吗? 嘿宝,我一定会检查【参考方案2】:

从 createDocument 返回承诺将允许 await createDocument(args1) 等到它完成执行

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()
)

【讨论】:

【参考方案3】:

使用 async-await 和 then/catch 是处理这类事情的坏方法。使用 await 你可以完成这项工作

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)
 

或者您可以删除 async/await 并简单地使用 then/catch

【讨论】:

以上是关于NodeJS Express API 调用结构异步事件的主要内容,如果未能解决你的问题,请参考以下文章

使用nodejs和rest api调用和express进行异步处理-序列错误

nodejs-在函数内执行多个异步调用的最佳方法?

Express 和 Nodejs:调用外部 API 的最佳方式

使用 Express 和 NodeJS 构建 REST API 的最佳实践

使用 fetch 在节点中进行异步 api 调用的最佳实践?

带有 Express 异步 API 调用的 Node.JS