你能解决 if 语句中的承诺吗?
can you resolve a promise in an if statement?
我对在 JS 中使用 promises 很陌生,我试图在另一个函数中执行更多代码之前先执行一个函数。唯一的问题是承诺的函数使用 if 语句来循环 setTimeout 命令。我添加了一个 if 语句以确保函数在我 resolve promise 之前完成循环,但 resolve 只是没有做任何事情。我使用 console.log 来确保 if 语句正在执行,并且在解析的任何一侧打印到控制台都没有问题。任何帮助将不胜感激。
代码:
async makeToast(loader, toaster){
toaster.texture = loader.resources['toaster_down'].texture;
this.interactive = false;
this.x = toaster.x;
this.y = toaster.y - 100;
let transform = {y: this.y};
let popDown = new TWEEN.Tween(transform)
.to({y: toaster.y - 50}, 200)
.onUpdate(() => this.y = transform.y);
popDown.start();
await this.changeTexture(loader, toaster.setting)
console.log('toasting done');
this.interactive = true;
}
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
setTimeout(() => {
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting) this.changeTexture(loader, setting);
else if(this.state == setting) resolve();
}, 1000);
});
}
在第一个 setTimeout
回调执行后,您将永远不会解析最外层调用的返回承诺。您最终将解决 innermost 调用返回的承诺,但这没有任何作用,因为从未使用过从那里返回的承诺。
你 可以 写 if (this.state < setting) resolve(this.changeTexture(loader, setting))
但我推荐一种不同的,更不令人困惑(和非递归)的方式:
// This could be defined globally, can be useful elsewhere too
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
// This is in your object
async changeTexture (loader, setting) {
while (this.state < setting) {
await delay(1000)
this.state++
this.texture = loader.resources[`bread${this.state}`].texture
}
}
这里我也制作了changeTexture
函数async
,所以我们可以在里面使用await
,从而以更直接的方式实现延迟,并且可以构建一个常规的 while
循环整个事情。
(注意:从技术上讲,您现有的代码无条件地执行第一次迭代,因此 do ... while
会更准确,但我假设这只是您尝试使用 [= 构建它的方式的结果12=] 而不是你真正需要的。)
您可以在代码中的任意位置调用 resolve
和 reject
。但是您必须从您的 Promise 中恰好调用其中一个。
当您的 if 条件为假时,您的示例代码不会执行此操作,因此您需要修复该问题。
只要有一个闭包将 Promise 构造函数中的 resolve
变量与您在 if
语句中调用的 resolve()
链接起来,您就可以。但是在你的代码中你没有这个:
class SomeClass {
// ...
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
setTimeout(() => {
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting)
this.changeTexture(loader, setting); <----------.
else if(this.state == setting) |
resolve(); <-- There is a closure to this /
}, 1000); /
}); .---------------------------'
} |
} However this function call will have it's own
"resolve" variable that is no longer captured
by this closure.
这意味着当 if/else
最终调用 resolve()
时 resolve 与调用 changeTexture()
时返回的 Promise 无关。
做你想做的事情的方法是不要递归地调用 changeTexture
,这样你就可以在 Promise 的 resolve
变量和你最终调用的 resolve
之间保持一个闭包。为此,只需将 setTimeout
回调与主 changeTexture
函数分开即可:
class SomeClass {
// ...
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
let loop = () => { // use arrow function to capture "this"
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting) setTimeout(loop, 1000);
else if(this.state == setting) resolve();
}
loop();
});
}
}
或者,为了对代码进行最少的更改,您只需更改一行即可使代码正常工作:
class SomeClass {
// ...
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
setTimeout(() => {
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting) setTimeout(arguments.callee(),1000); // <----THIS
else if(this.state == setting) resolve();
}, 1000);
});
}
}
arguments.callee
变量指向您传递给 setTimeout
的 () => {...
函数。但是,arguments.callee
已被弃用并在严格模式下被禁用,因此请尽可能使用上面的 loop
函数。
我对在 JS 中使用 promises 很陌生,我试图在另一个函数中执行更多代码之前先执行一个函数。唯一的问题是承诺的函数使用 if 语句来循环 setTimeout 命令。我添加了一个 if 语句以确保函数在我 resolve promise 之前完成循环,但 resolve 只是没有做任何事情。我使用 console.log 来确保 if 语句正在执行,并且在解析的任何一侧打印到控制台都没有问题。任何帮助将不胜感激。
代码:
async makeToast(loader, toaster){
toaster.texture = loader.resources['toaster_down'].texture;
this.interactive = false;
this.x = toaster.x;
this.y = toaster.y - 100;
let transform = {y: this.y};
let popDown = new TWEEN.Tween(transform)
.to({y: toaster.y - 50}, 200)
.onUpdate(() => this.y = transform.y);
popDown.start();
await this.changeTexture(loader, toaster.setting)
console.log('toasting done');
this.interactive = true;
}
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
setTimeout(() => {
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting) this.changeTexture(loader, setting);
else if(this.state == setting) resolve();
}, 1000);
});
}
在第一个 setTimeout
回调执行后,您将永远不会解析最外层调用的返回承诺。您最终将解决 innermost 调用返回的承诺,但这没有任何作用,因为从未使用过从那里返回的承诺。
你 可以 写 if (this.state < setting) resolve(this.changeTexture(loader, setting))
但我推荐一种不同的,更不令人困惑(和非递归)的方式:
// This could be defined globally, can be useful elsewhere too
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
// This is in your object
async changeTexture (loader, setting) {
while (this.state < setting) {
await delay(1000)
this.state++
this.texture = loader.resources[`bread${this.state}`].texture
}
}
这里我也制作了changeTexture
函数async
,所以我们可以在里面使用await
,从而以更直接的方式实现延迟,并且可以构建一个常规的 while
循环整个事情。
(注意:从技术上讲,您现有的代码无条件地执行第一次迭代,因此 do ... while
会更准确,但我假设这只是您尝试使用 [= 构建它的方式的结果12=] 而不是你真正需要的。)
您可以在代码中的任意位置调用 resolve
和 reject
。但是您必须从您的 Promise 中恰好调用其中一个。
当您的 if 条件为假时,您的示例代码不会执行此操作,因此您需要修复该问题。
只要有一个闭包将 Promise 构造函数中的 resolve
变量与您在 if
语句中调用的 resolve()
链接起来,您就可以。但是在你的代码中你没有这个:
class SomeClass {
// ...
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
setTimeout(() => {
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting)
this.changeTexture(loader, setting); <----------.
else if(this.state == setting) |
resolve(); <-- There is a closure to this /
}, 1000); /
}); .---------------------------'
} |
} However this function call will have it's own
"resolve" variable that is no longer captured
by this closure.
这意味着当 if/else
最终调用 resolve()
时 resolve 与调用 changeTexture()
时返回的 Promise 无关。
做你想做的事情的方法是不要递归地调用 changeTexture
,这样你就可以在 Promise 的 resolve
变量和你最终调用的 resolve
之间保持一个闭包。为此,只需将 setTimeout
回调与主 changeTexture
函数分开即可:
class SomeClass {
// ...
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
let loop = () => { // use arrow function to capture "this"
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting) setTimeout(loop, 1000);
else if(this.state == setting) resolve();
}
loop();
});
}
}
或者,为了对代码进行最少的更改,您只需更改一行即可使代码正常工作:
class SomeClass {
// ...
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
setTimeout(() => {
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting) setTimeout(arguments.callee(),1000); // <----THIS
else if(this.state == setting) resolve();
}, 1000);
});
}
}
arguments.callee
变量指向您传递给 setTimeout
的 () => {...
函数。但是,arguments.callee
已被弃用并在严格模式下被禁用,因此请尽可能使用上面的 loop
函数。