如何解决如果 setTimeout 是 when 链的一部分,为什么它们会并行执行?
在探索 then
链的过程中,我遇到了两件我无法解释的事情。
这是代码:
let x1 = new Promise((resolve) => {
setTimeout(() => {
console.log('10 seconds first timeout');
resolve(1);
},10000)
});
let x2 = x1.then((p1) => {
setTimeout(() => {
console.log('10 seconds second timeout');
console.log(p1);
return ('2');
},10000);
});
let x3 = x2.then((p2) => {
setTimeout(() => {
console.log('10 seconds third timeout');
console.log(p2);
return ('3');
},10000);
});
console.log(x1,x2,x3);
问题是:
-
x2.then
回调会在x1.then
回调打印后立即打印。但是x2.then
会得到一个超时 10 秒的回调,那么为什么在打印前不等待 10 秒呢?第一个和第二个之间有 10 秒,正如预期的那样。 -
x1.then
回调返回一个字符串'2'
,据我所知,这个值应该是返回的Promise
的值(由Promise
包裹),那么为什么在尝试打印到控制台console.log(p2)
时它会打印 undefined 而不是字符串'2'
?
解决方法
x1
看起来不错——您已经承诺回调,因此 x2
在调用 resolve
之前不会触发。但是 x2
和 x3
不会做您想做的事,因为它们不返回承诺。 return
回调中的 setTimeout
与主线承诺链无关,它们只是从回调块返回到忽略返回值的作用域。
鉴于一般的 x1
模式,您可以将 promisified 计时器放入一个函数中并在其上链接 .then
以实现所需的行为:
const timeout = (cb,ms) =>
new Promise(resolve => setTimeout(() => resolve(cb()),ms))
;
timeout(() => console.log("a"),0)
.then(() => timeout(() => console.log("b"),1000))
.then(() => timeout(() => console.log("c"),1000))
;
console.log("this prints first always because it's synchronous");
就主要的 console.log
而言,您的方法将不起作用,因为您无法将异步代码转换为同步代码。所有同步代码必须在 JS 开始处理异步任务队列之前完成运行。您必须通过 await
、then
或使用回调来获取您想要的值,但您可以重构上述代码以通过在回调中返回某些内容来处理通过 resolve
调用传递的值并为下一个 then
回调添加一个参数。
const timeout = (cb,ms) =>
new Promise((resolve,reject) =>
setTimeout(() => {
try {
resolve(cb());
}
catch (err) {
reject(err);
}
},ms)
)
;
timeout(() => {
console.log("a");
return "b";
},0)
.then(val =>
timeout(() => {
console.log(val);
return "c";
},1000)
)
.then(val =>
timeout(() => console.log(val),1000)
)
.then(() =>
timeout(() => {
throw Error("whoops");
},1000)
)
.catch(err => console.error(err.message))
;
正如Bergi 在评论中提到的,您需要try
/catch
或将回调从返回的promise 链接起来,而不是将其传入,以便能够正确处理回调抛出的可能性。上面的代码通过在 promise 中调用 reject
来说明这一点。
Bergi 还建议从超时返回值是一种反模式。您可以延迟执行下一个承诺而不返回,例如:
const wait = ms =>
new Promise(resolve => setTimeout(resolve,ms))
;
Promise
.resolve()
.then(() => {
console.log("a");
return wait(1000);
})
.then(() => {
console.log("b");
return wait(1000);
})
.then(() => {
console.log("c");
})
.catch(err => console.error(err))
;
无论哪种方式,对于这个用例,async
/await
都应该更简洁,特别是如果您想沿着 Promise 链传递值:
const wait = ms =>
new Promise(resolve => setTimeout(resolve,ms))
;
(async () => {
console.log("a");
await wait(1000);
console.log("b");
await wait(1000);
console.log("c");
})();
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。