Javascript 在Node.js上运行MySQL2执行查询时,在\u结束后出错\u STREAM\u WRITE\u

Javascript 在Node.js上运行MySQL2执行查询时,在\u结束后出错\u STREAM\u WRITE\u,javascript,node.js,async-await,data-access-layer,mysql2,Javascript,Node.js,Async Await,Data Access Layer,Mysql2,我正在实现一个功能,只要点击一个按钮,就可以通过向后端发送请求并使用更新数据库来延长用户会话的寿命 为此,我编写了以下前端代码: onClose:函数操作{ 试一试{ 如果OAAction==是{ 让reqURL=/sessionExtend; 设reqData={ 会话id:sessionStorage.getItemSessionId, 用户id:sessionStorage.getItemUserId }; 让callbackOK=函数响应数据{ curr.onSuccessfulResp

我正在实现一个功能,只要点击一个按钮,就可以通过向后端发送请求并使用更新数据库来延长用户会话的寿命

为此,我编写了以下前端代码:

onClose:函数操作{ 试一试{ 如果OAAction==是{ 让reqURL=/sessionExtend; 设reqData={ 会话id:sessionStorage.getItemSessionId, 用户id:sessionStorage.getItemUserId }; 让callbackOK=函数响应数据{ curr.onSuccessfulResponsecurr、responseData、sessionExtendSuccess、sessionExtendFail、false; }; 让callbackErr=函数响应数据{ curr.onErrorResponsecurr、responseData、sessionExtendFail; }; 当前性能请求、请求数据、回调确定、回调错误; } }犯错误{ console.logerr; MessageToast.showsMsg; } } 该请求由app.js接收,app.js使用MySQL2建立数据库连接,并将请求转发给DAL:

app.post/sessionExtend,异步函数请求,res{ 让session_id=req.body.session_id; 让user_id=req.body.user_id; 设con=DAL.getConnection; res.setHeaderContent-Type,application/json; 试一试{ const response=await DAL.sessionExtendcon,session\u id,user\u id; res.sendJSON.stringify{ 结果:对,, 消息:会话已扩展 }; }抓住e{ res.sendJSON.stringify{ 结果:假,, 消息:无法扩展会话 }; } con.close; }; DAL模块执行SQL查询,并应返回成功或错误的结果:

sessionExtend:异步函数sessionExtendcon,会话id,用户id{ con.connectfunction错误{ 试一试{ 如果犯了错误,就扔出错误; con.queryqryDict.SQL_querys.setupdateextendedsession[session_id,user_id],函数错误{ 让结果; 如果出错{ 结果=JSON.stringify{ 结果:假,, 信息:失败 }; }否则{ 结果=JSON.stringify{ 结果:对,, 信息:成功 }; } 返回结果; }; }犯错误{ 让result=JSON.stringify{ 结果:假,, 信息:呃 }; 返回结果; } }; }, 问题是,当我在调试器中执行此代码时,会出现异常:

ERR\u STREAM\u WRITE\u AFTER\u END错误[ERR\u STREAM\u WRITE\u AFTER\u END]:写入 结束后 在Socket.Writable.write\u stream\u Writable.js:297:11 在Connection.write C:\Users\User\IdeaProjects\TST\node\u modules\mysql2\lib\Connection.js:226:17 在Connection.writePacket C:\Users\User\IdeaProjects\TST\node\u modules\mysql2\lib\Connection.js:271:12 在ClientHandshake.sendCredentials C:\Users\User\IdeaProjects\TST\node\u modules\mysql2\lib\commands\client\u handshake.js:64:16 在ClientHandshake.handshake单元C:\Users\User\IdeaProjects\TST\node\u modules\mysql2\lib\commands\client\u handshake.js:137:12 在ClientHandshake.execute C:\Users\User\IdeaProjects\TST\node\u modules\mysql2\lib\commands\command.js:39:22 在Connection.handlePacket C:\Users\User\IdeaProjects\TST\node\u modules\mysql2\lib\Connection.js:417:32 在PacketParser.onPacket C:\Users\User\IdeaProjects\TST\node\u modules\mysql2\lib\connection.js:75:12 在PacketParser.executeStart C:\Users\User\IdeaProjects\TST\node\u modules\mysql2\lib\packet\u parser.js:75:16 在插座上。C:\Users\User\IdeaProjects\TST\node\u modules\mysql2\lib\connection.js:82:25

我还注意到,在调试过程中,我首先在前端得到后端的响应,然后才使用con.queryqryDict.SQL_querys.setupdateextendedsession、[session_id,user_id],function err{…}到达DAL中的断点

我的问题是:

为什么我会在结束后出错?如何避免

为什么我首先在前端从后端得到响应,然后才到达DAL中的断点?我假设wait DAL.sessionExtendcon、session\u id、user\u id应该等到DAL上的任务完成,承诺得到解决

简而言之:您没有等待con.connection和con.query,因此外部代码继续并调用con.close并返回前端结果,稍后con.query尝试通过现在已关闭的连接发送查询,导致此异常

您正在编写异步函数,但只使它们半异步

例如,这将不起作用:

异步函数getStuff{ stuff.getfunc 错误、数据{ 如果发生错误,抛出错误//终止进程! return data.stuff//不返回任何地方 } } //稍后: const stuff=等待getStuff console.logstuff//打印未定义! …因为本质上,异步函数只是同步调用另一个函数而不等待它,然后立即不返回任何内容,即未定义:

异步函数getStuff{ 东西,拿。。。 //正如您所看到的,getStuff中没有返回 } 稍后,您传递的回调将运行,但是您的外部代码的序列已经离开了平台

您想要做的是拥有stuff.get返回一个承诺大多数现代库都会这样做,即使它们另外公开了一个回调承诺以与旧代码库兼容,并等待它:

异步函数getStuff{ const data=wait stuff.get//等待内容返回 return data.stuff//实际返回内容 //“if err throw err”现在也变得不必要了 } //稍后: const stuff=等待getStuff console.logstuff//打印内容! 如果您的SQL库将公开promise接口,您只需等待它。您写道您正在使用mysql2。如果需要使用require'mysql2/promise',此库有一个promise接口。我建议切换到承诺接口而不是回调接口

还有一种方法可以将现有的con连接升级到promise接口:con.promise。因此,您只需执行con=DAL.getConnection.promise而不是con=DAL.getConnection

然后,您可以像这样重写代码或等效代码,具体取决于您选择的库:

异步函数sessionExtendcon,会话id,用户id{ 试一试{ 等待连接 等待con.queryqryDict.SQL_querys.setUpdateExtendSession,[会话id,用户id] 返回JSON.stringify{result:true,消息:'success'} }犯错误{ 返回JSON.stringify{result:false,message:err.toString} } } 编辑:下面的部分实际上已经过时了,因为mysql2允许升级到promise接口的现有连接,但我还是把它留在这里,以防它在类似情况下帮助其他人

如果您不能切换到promise接口,您可以改为Promisis现有调用,但它看起来有点复杂:

const{promisify}=require'util' 异步函数sessionExtendcon,会话id,用户id{ 试一试{ 等待promisifycon.connect.callcon 等待promisifycon.query.callcon,qryDict.SQL_querys.setupdateextendedsession,[会话id,用户id] 返回JSON.stringify{result:true,消息:'success'} }犯错误{ 返回JSON.stringify{result:false,message:err.toString} } } util.promisify包装了一个需要err数据回调的函数,将其转换为一个返回承诺的异步函数。由于con.query等都是con上的方法,所以它们需要保持上下文,这就是为什么我写promisifycon.query.callcon。。。简而言之:您没有等待con.connection和con.query,因此外部代码继续调用con.close并返回前端结果,稍后con.query尝试通过现在关闭的连接发送查询,导致此异常

您正在编写异步函数,但只使它们半异步

例如,这将不起作用:

异步函数getStuff{ stuff.getfunction错误,数据{ 如果发生错误,抛出错误//终止进程! return data.stuff//不返回任何地方 } } //稍后: const stuff=等待getStuff console.logstuff//打印未定义! …因为本质上,异步函数只是同步调用另一个函数而不等待它,然后立即不返回任何内容,即未定义:

异步函数getStuff{ 东西,拿。。。 //正如您所看到的,getStuff中没有返回 } 稍后,您传递的回调将运行,但是您的外部代码的序列已经离开了平台

您想要做的是拥有stuff.get返回一个承诺大多数现代库都会这样做,即使它们另外公开了一个回调承诺以与旧代码库兼容,并等待它:

异步函数getStuff{ const data=wait stuff.get//等待内容返回 return data.stuff//实际返回内容 //“if err throw err”现在也变得不必要了 } //稍后: const stuff=等待getStuff console.logstuff//打印内容! 如果您的SQL库将公开promise接口,您只需等待它。您写道您正在使用mysql2。如果需要使用require'mysql2/promise',此库有一个promise接口。我建议切换到承诺接口而不是回调接口

还有一种方法可以将现有的con连接升级到promise接口:con.promise。所以您只需执行con=DAL.getConnec tion.promise而不是con=DAL.getConnection

然后,您可以像这样重写代码或等效代码,具体取决于您选择的库:

异步函数sessionExtendcon,会话id,用户id{ 试一试{ 等待连接 等待con.queryqryDict.SQL_querys.setUpdateExtendSession,[会话id,用户id] 返回JSON.stringify{result:true,消息:'success'} }犯错误{ 返回JSON.stringify{result:false,message:err.toString} } } 编辑:下面的部分实际上已经过时了,因为mysql2允许升级到promise接口的现有连接,但我还是把它留在这里,以防它在类似情况下帮助其他人

如果您不能切换到promise接口,您可以改为Promisis现有调用,但它看起来有点复杂:

const{promisify}=require'util' 异步函数sessionExtendcon,会话id,用户id{ 试一试{ 等待promisifycon.connect.callcon 等待promisifycon.query.callcon,qryDict.SQL_querys.setupdateextendedsession,[会话id,用户id] 返回JSON.stringify{result:true,消息:'success'} }犯错误{ 返回JSON.stringify{result:false,message:err.toString} } } util.promisify包装了一个需要err数据回调的函数,将其转换为一个返回承诺的异步函数。由于con.query等都是con上的方法,所以它们需要保持上下文,这就是为什么我写promisifycon.query.callcon。。。在CherryDT的帮助下,这个问题已经通过切换到MySQL2-的ES7异步/等待包装版本得到了解决,而不仅仅是promisifycon.query…

为节省其他公众的时间,最终的即用代码:

app.js

app.post/sessionExtend,异步函数请求,res{ 让session_id=req.body.session_id; 让user_id=req.body.user_id; const con=wait DAL.getConnection; res.setHeaderContent-Type,application/json; const response=await DAL.sessionExtendcon,session\u id,user\u id; res.sendJSON.stringify{ 结果:response.result, message:response.message }; 等待监狱关闭; }; DAL.js

sessionExtend:异步函数sessionExtendcon,会话id,用户id{ 让结果; const[rows,fields]=wait con.executeqryDict.SQL_querys.setupdateextendedsession[session_id,user_id]; 如果rows.warningStatus==0{ 结果={ 结果:对,, 消息:会话已扩展 }; }否则{ 结果={ 结果:假,, 消息:会话未扩展 }; } 返回结果; }, 正如您所看到的,现在代码更易于理解和维护

另外,我的建议是:使用async/await,它们非常好,并且尽量避免回调。

在CherryDT的帮助下,通过切换到MySQL2-的ES7 async/await包装版本,问题已经解决

为节省其他公众的时间,最终的即用代码:

app.js

app.post/sessionExtend,异步函数请求,res{ 让session_id=req.body.session_id; 让user_id=req.body.user_id; const con=wait DAL.getConnection; res.setHeaderContent-Type,application/json; const response=await DAL.sessionExtendcon,session\u id,user\u id; res.sendJSON.stringify{ 结果:response.result, message:response.message }; 等待监狱关闭; }; DAL.js

sessionExtend:异步函数sessionExtendcon,会话id,用户id{ 让结果; const[rows,fields]=wait con.executeqryDict.SQL_querys.setupdateextendedsession[session_id,user_id]; 如果rows.warningStatus==0{ 结果={ 结果:对,, 消息:会话已扩展 }; }否则{ 结果={ 结果:假,, 消息:会话未扩展 }; } 返回结果; }, 正如您所看到的,现在代码更易于理解和维护


另外,我的建议是:使用async/await,它们非常好,并且尽量避免回调。

您没有在等待con.connect和con.query调用。如果您的SQL库有一个promise接口,请使用它并等待它。否则,使用require'util.promisify将其转换为一个。流结束后写入是因为在con.query运行之前执行con.close。@CherryDT,我假设DAL.getConnection与此类似,它包装了MySQL2的createConnection…我将选中require'util.promisify。关于con.query…,我使用了一个回调函数,当查询完成时应该会到达这个回调函数。是的,但是您的sessionExtend函数早就在回调函数执行时返回了。下面是我的答案。我只是在您指定要使用MySQL2之后才看到。正在编辑我的答案。您没有在等待con.connect和con.query呼叫。如果你的SQL天秤座
ry有一个promise接口,使用它并等待它。否则,使用require'util.promisify将其转换为一个。流结束后写入是因为在con.query运行之前执行con.close。@CherryDT,我假设DAL.getConnection与此类似,它包装了MySQL2的createConnection…我将选中require'util.promisify。关于con.query…,我使用了一个回调函数,当查询完成时应该会到达这个回调函数。是的,但是您的sessionExtend函数早就在回调函数执行时返回了。下面是我的答案。我只是在您指定要使用MySQL2之后才看到。编辑我的答案。谢谢你的详细解释。我使用的是,在他们建立连接的代码片段中,没有提到承诺是强制性的,事实并非如此。但是如果不使用它们,就必须在整个过程中使用回调,而您没有这样做,这就是代码无法工作的原因。如果你这样做了,代码会工作,但也会变得更加复杂。这就像用信用卡支付你的订阅费可能并不总是强制性的,但它非常有帮助,因为否则你每个月都要用现金支付;我更新了我的答案,以反映mysql2tanks中存在的选项,进行了大量的详细解释,并指出,这确实可以消除回调地狱,保持代码干净清晰。感谢您的详细解释。我使用的是,在他们建立连接的代码片段中,没有提到承诺是强制性的,事实并非如此。但是如果不使用它们,就必须在整个过程中使用回调,而您没有这样做,这就是代码无法工作的原因。如果你这样做了,代码会工作,但也会变得更加复杂。这就像用信用卡支付你的订阅费可能并不总是强制性的,但它非常有帮助,因为否则你每个月都要用现金支付;我更新了我的答案,以反映mysql2tanks中存在的选项,并对其进行了详细的解释,并指出,这确实可以消除回调地狱,保持代码干净清晰。