JavaScript类链式异步方法嵌套await如何优化为简洁调用
解决方案
问题本质
你当前的Upload/CreateJob/GetJob都是async方法,调用后会直接返回Promise对象,但自定义的业务方法都定义在Transcribe类的原型上,Promise实例本身没有这些方法,所以才必须每一层都await拿回类实例,才能调用下一个方法,最终出现三层嵌套await的写法。
另外你类构造函数里写的this.Upload = this.Upload、this.CreateJob = this.CreateJob这类代码完全没有实际作用,可以直接删除。
改造方法
不需要在路由层写.then链式调用,也不需要嵌套await,只需要给类加内部异步队列,让每个业务方法调用时把逻辑排队、返回实例本身支持链式,再给类加thenable支持让整个链式调用可以被直接await即可。
改造后的AmazonS3基类
class AmazonS3 { constructor() { // 内部异步队列,初始为已完成状态的Promise this._chain = Promise.resolve(); } Upload(file, bucket) { // 将上传逻辑追加到队列尾部 this._chain = this._chain.then(async () => { const uploadParams = { Bucket: bucket, Body: fs.createReadStream(file.path), Key: file.filename, } this.data = await s3.upload(uploadParams).promise() }) // 返回实例本身,支持链式调用 return this } // 自定义then方法,符合thenable约定,支持直接await实例 then(resolve, reject) { return this._chain .then(() => resolve(this.data)) .catch(reject) } }
改造后的Transcribe子类
class Transcribe extends AmazonS3 { CreateJob(params) { // 将创建转录任务逻辑追加到队列尾部 this._chain = this._chain.then(async () => { if(this.data?.Location) { params.Media = { ...params.Media, MediaFileUri: this.data.Location } } this.data = await transcribeService.startTranscriptionJob(params).promise() }) return this } GetJob(jobName) { // 将查询任务状态逻辑追加到队列尾部 this._chain = this._chain.then(async () => { if(this.data?.TranscriptionJob?.TranscriptionJobName) { jobName = this.data.TranscriptionJob.TranscriptionJobName } this.data = await transcribeService.getTranscriptionJob({TranscriptionJobName: jobName}).promise() }) return this } }
改造后的路由代码
完全符合你预期的调用形式,只需要在最外层加一次await即可:
routes.post('/aws', upload.single('file'), async (req, res) => { const transcribeParams = JSON.parse(req.body.options) const bucket = 'bucket-name' const data = await awsTranscribe.Upload(req.file, bucket).CreateJob(transcribeParams).GetJob() res.send(data) })
注意事项
如果你的awsTranscribe是全局单例、会被多个请求复用,会出现异步队列串扰的问题(多个请求的操作排到同一个队列里),这种场景有两种解决方式:
- 每次请求进来时新建一个
Transcribe实例,用完即抛,是最稳妥的方案 - 给类加一个重置方法,每次调用链开始前重置内部队列:
reset() { this._chain = Promise.resolve(); this.data = null; return this }
调用时先调reset即可:const data = await awsTranscribe.reset().Upload(...).CreateJob(...).GetJob()
内容的提问来源于stack exchange,提问作者Nekit
相关产品推荐
相关产品推荐

