# 背景
最近遇到一个问题,具体的场景是,接口 A
返回一段可以让前端渲染的 html
字符串,这个字符串里包含里一个可播放的 video
的地址,但是地址是不可播放的,需要替换为另外一个可播放的域名。
# 处理流程
解决方式:先请求接口 A
获取 html
字符串,从中截取 video
的 src
属性,再调接口 B
将接口转换为可播放的 url
,最后拿到新的 url
之后再通过正则的 replace
方法将对应的 url
替换成新的 url
,然后再处理剩下的逻辑。
其实捋下来之后发现无非就是 A
的回调结果里调 B
接口,但是即便是嵌套一层,也不够优雅,于是在想利用 Promise
的串行处理,将回调打平,Promise
的处理无非也就是先去调用 A
接口,获取数据之后将结果传递出去再通过.then
拿到返回值数据,在.then
里再调新接口 B
从而解决问题。虽然后来确实通过 Promise
的链式调用解决了问题,但是这个过程中也有一些疑惑,就是如题。
# 拆解和理解
根据 es6
的 resolve
的理解,其函数的作用是,将 Promise
对象的状态从“未完成”变为“成功”(即从 pending
变为 resolved
),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去,如果我们不 resolve
,则数据一直被保存在上一个 Promise
的函数中。经过整理重新学习后发现,如果需要将 Promise
的返回的结果在当前函数的.then
里一直按照次序往下传递,其是必须要在上一步的 then
里将结果 return
才能在下一个 then
里接住,一旦在某一个 then
里 resolve
了,则在 resolve
的那个 then
后面的 then
中虽然后面的逻辑会执行,但是获取不到上一次计算的结果,反而是直接在 resolve
的 then
里将结果从当前函数传递出去了。
# 示例
具体用一个 🌰 来看:
假设现在我们有一个 sleep
的 Promise
的模拟异步方法,传递不同的 ms
数代表分别代表 A、B
俩接口。
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// 这是获取新url的接口B
var urlConvert = (url) =>
new Promise((resolve, reject) => {
console.log(`我们现在获取到了旧的url:${url} 要去请求新的`);
sleep(500).then((res) => {
if (true) {
resolve("http//newVideoUrl.com");
} else {
reject("发生错误");
}
});
});
var handleData = () =>
new Promise((resolve, reject) => {
// 接口A
var initUrl = sleep(1000).then((res) => "http://oldVideoUrl.com");
initUrl.then((res) => urlConvert(res)).then((res) => resolve(res));
});
handleData().then((url) => {
console.log("url", url);
});
这里我们能发现在 handleData
内部,我是需要先处理完数据,在最后才将处理完的数据 resolve
,所以可以看到是前边通过箭头函数一步一步将上一次处理的结果 return
了,所以在下一个 then
里才获取到处理完成的数据,并且我们在最后一步通过 resolve
将结果返回,当我们在外部调用 handleData
之后,链式调用的结果是可以获取到 url
的值。
但是如果我们将最后一步结果依旧改成 return
的形式,我们再来看一下效果。
- initUrl.then((res) => urlConvert(res)).then((res) => resolve(res));
+ initUrl.then((res) => urlConvert(res)).then((res) => res);
handleData().then((url) => {
console.log("url", url);
});
再次运行后,发现没有打印也就是没走到这里,说明如果直接 return
的话是无法从原来的函数里将 Promise
的结果传递出去的。
# 结论
这里基本上是更清楚了,如果不需要将结果传递出去,在 then
处理完成之后 return value
就好了,如果需要传递出去就将数据 resolve(value)
出去,至于 resolve
的时候要不要 加 return
就看还要不要处理 resolve
后面的逻辑,一般来说立即 resolved
的 Promise
是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
所以如果上述例子改为
- initUrl.then((res) => urlConvert(res)).then((res) => resolve(res));
+ initUrl.then((res) => urlConvert(res)).then((res) => {
+ resolve(res)
+ console.log('resolve后,打印url前')
+});
便会在 resolve
的那次事件循环后,并且会在新的一轮 handleData()
前执行。