handleGroups() {
Promise.all(
this.groups.map(g => {
g.videoDtos = []
g.videos.split(',').map(async (id) => {
let v = await this.getVideoById(Number(id));
if (v) g.videoDtos.push(v)
})
})
).then(res => {
this.playGroups()
})
}
这里理想的执行顺序是当 g.videoDtos得到所有的结果,再去执行playGroups(),但是事实是当执行到g.videos.split(',').map(async (id)这一步,就直接跳到了playGroups(),然后再去执行getVideoById(Number(id))
getVideoById(id: number) {
return new Promise((resolve, reject) => {
getVideoDto(id).then(res => {
if (res.data.code == 200) {
let video: Video = res.data.data
resolve(video)
} else {
reject();
}
})
})
}
playGroups() {
let videos = this.groups[this.playGroupsIndex].videoDtos;
// 放一堆视频
if (videos.length > 0)
this.$refs.videoBoxRef.setVideos(videos);
this.playGroupsTimer = setTimeout(()=>{
this.playGroupsIndex = (this.playGroupsIndex + 1) % this.groups.length;
this.playGroups();
}, this.roundTime * 1000);
}
想问下,如何才能让这个链式调用起作用,等getVideoById(Number(id))都执行完之后,再去执行.then中的playGroups()
先看注释
然后一步步分析
首先,既然内部用到了
await
,那外面也没必要客气,用await
可以逻辑更容易懂,所以不过
res
拿到之后并没有使用,不需要,可以直接把const res =
丢掉。接下来
Promise.all()
的参数是一个数组,如果数组的元素是 Promise 对象,会等待它resolve()
;如果是普通对象或undefined
等,就不需要等待。Promise.all()
会返回一个Promise
对象,它会在参数数组中所有 Promise 对象都 resolved 之后resolve()
。不妨拆一下这个调用,变成
现在的问题是
groupPromises
是什么东西。它既然是从this.group.map(fn)
返回的,那一定是一个数组,而其中有元素,是fn
执行后的返回值。这里fn
是g => { ... }
,很显然,没有返回值,所以groupPromises
现在是由一堆undefined
组成的数组。显然这不是需要的结果,应该它
g => { ... }
加上返回值,而且这个返回值得是 Promise 对象才对得起Promise.all()
。那该返回什么?接下来看
g => { ... }
的内部逻辑。它的内部是要异步处理某个 group 中的若干 videos。同上面类似的分析,可以知道
g.videos.split(",").map(async () => {})
返回的是一个由若干 Promise 对象组成的数组。需要注意的是this.getVideoById(Number(id))
虽然返回 Promise 对象,但是已经有await
等过了;但是它外面包的async () => {}
会产生另一个 Promise 对象,也就是map
的返回值,并没有谁在等。当然可以用for
循环来等(注意不是.forEach()
)不过既然是并发处理,可以用Promise.all()
组织起来一起等,所以可以改为好了,注意,这里的
await
不是修饰的map()
回调,它的外层函数需要使用async
,所以g => { ... }
要变成async g => { ... }
。是不是顺手就把g => { ... }
的返回值变成 Promise 对象了(前面说过需要返回 Promise 对象的)?所有逻辑都分析过多了,把整个过过程连起来,就是正确的结果