关于controller中的写数据库操作的疑问!
发布于 3 年前 作者 Thetiso 3435 次浏览 来自 问答

假设我有一个api接口如下:

async updateNameById(@Query() id: number) {
	let dbUsr = await this.usrService.findById(id)
	//其他操作
	await this.usrService.updateXXX({id, name})
	await this.logService.log(xxxx)
	return 'done'
}

从这个api的用时角度考虑 我们知道第一行findById一般数据库读都是很快的,真正影响这个接口速度的是updateXXX(数据库写操作) 那么:

  1. 这个update的方法前的await可以取消吗? (试了下,确实api的往返时间快了很多)
  2. 无关乎业务的log是不是可以取现await? (一般log都是写在middleware中,这里只是示意)

如果可以,取消await对整个服务有没有影响,比如说log的过程中,新的api请求到来了。

thx!

7 回复

可以取消 取消的后果就是你不知道执行是否出错,因为await会往外抛异常,你可以try catch捕获,如果取消await,那么你想关注是否出错了只能用.then()的方式,否则它就会把err送到unhandledRejection 如果你即不关心执行的结果,也不关心执行是否出错,那么也就无所谓了 这就是nodejs里面所谓的并发应用了,你啥都await那跟写同步代码没有区别

如果无前后关联可以使用 Promise.all

首先你得明白 await 是干什么的:
简单来说,加上 await 之后,程序会先执行更新(updateXXX),等待更新完成之后,再 logService.log(xxxx)
如果不加 await,程序会发出更新的命令,而不等待完成,然后立即 logService.log(xxxx)
就有可能发生:前端已经收到return 'done',而 updateXXX 还没完成,或已经出错了

所以,从代码的层面来讲,是可以取消的(也就是说,代码在语法方面没有问题)
但是从业务上讲,就要看具体需求了,如果不需要考虑 updateXXX 成功与否,那么 await 是可以不写的
但是对一般的需求,都需要加上 await

另外,你的最后一句,是另一个大问题,事务,很难一两句讲清

  1. 这个update的方法前的await可以取消吗? (试了下,确实api的往返时间快了很多)

可以, 只要是非关键性操作

  1. 无关乎业务的log是不是可以取现await? (一般log都是写在middleware中,这里只是示意)

同上, 可以

最后, 去看看await转换成的es5代码吧

await存在与否, 只会影响到是否多出一段 case: return(更进一步 迭代器是否有多1个步骤)

await右边的表达式仍旧会正常运行


另, 根据你的业务逻辑, 你应当让这几个操作完全并行

async updateNameById(@Query() id: number) {
	let pmsDbUsr = this.usrService.findById(id)
	//其他操作
	let pmsUpdate = this.usrService.updateXXX({id, name}) // 此处name 如果不依赖于 pmsDbUsr 的完成, 则 pmsDbUsr 就不用await
	let pmsLog =  this.logService.log(xxxx)
	
	// 此时已经完全并行执行了
	let DbUsr  = await pmsDbUsr // 等待主要操作响应
	await Promise.race([delay(100), pmsUpdate]) // 等待次主要操作响应, 最长100ms, 没结果就忽略
	await pmsLog // pmsLog 次要操作根据需要是否等待
	return 'done'
}

await的最佳实践是 尽量延迟到要用 右边表达式的返回值时, 再 await

这样可以充分并行

回到顶部