尝试在承诺中设置递归函数
Trying to set a recursive function inside a promise
let cancelDownload = true
const delay = () => {
return new Promise(() => {
if (cancelDownload === true){
setTimeout(() => {
console.log('Delaying...');
delay();
}, 1000);
}
else return null;
});
};
const cancelJob = async() => {
for(let i = 6; i>0; i--){
console.log('inside for ',i);
setTimeout(()=>{
cancelDownload = false
},4000)
await delay()
console.log('aaaaaaaa');
console.log(`the number is ${i}`);
}
}
cancelJob()
我正在尝试编写一个延迟函数,一旦满足条件,延迟就会被删除并恢复所有代码,但似乎一旦延迟完成,代码就会退出而不执行最后两个控制台日志
你传递给 promise 构造函数(promise 执行器 函数)的函数被调用时带有两个参数:一个用于解析 promise 的函数和一个用于拒绝它的函数(通常称为 resolve
和 reject
)。当您的异步工作完成时,您的代码应该调用这些函数之一。
你的不是,所以承诺永远不会兑现,你的代码永远等待 await
。
但还有其他问题:
1。如果您再次调用 delay
,它会创建一个 new 承诺。您使用 await
的代码只能访问第一个承诺,而不能访问那些递归调用创建的承诺。根本没有任何理由在这里使用递归。
2。对该函数的所有调用共享 相同的 标志。因此,如果我们通过不履行承诺来解决问题,循环会等待,但只会等待一次:
let cancelDownload = false;
const delay = () => {
return new Promise((resolve) => {
tick();
function tick() {
// Cancelled?
if (cancelDownload) {
// Yes, fulfill the promise
console.log("Flag is set, fulfilling");
resolve(null);
} else {
// No, wait another second
console.log("waiting 1000 and checking again");
setTimeout(tick, 1000);
}
}
});
};
const cancelJob = async () => {
setTimeout(() => {
console.log("Setting cancelDownload to true");
cancelDownload = true;
}, 4000);
for (let i = 6; i > 0; i--) {
console.log("inside for ", i);
await delay();
console.log("aaaaaaaa");
console.log(`the number is ${i}`);
}
};
cancelJob();
.as-console-wrapper {
max-height: 100% !important;
}
不过,我可能会误认为第二个是您的用例的问题,因为在对现已删除的答案的评论中,您说您希望循环只等待一次(“整个循环”而不是不仅仅是一次迭代)。
如果你想要一个轮询标志的函数(我不推荐它,轮询通常不是最佳实践,虽然有时你无法避免它)并在设置时履行承诺,你可以使用 AbortController
:
const delay = (signal) => {
let done = false;
return new Promise((resolve) => {
tick();
function tick() {
// Cancelled?
if (signal.aborted) {
// Yes, fulfill the promise with null
console.log("Fulfilling with null");
resolve(null);
} else {
// No, wait another second
console.log("Waiting 1000 and checking again");
setTimeout(tick, 1000);
}
}
});
};
const cancelJob = async () => {
const controller = new AbortController();
setTimeout(() => {
console.log("Cancelling");
controller.abort();
}, 4000);
for (let i = 6; i > 0; i--) {
console.log("inside for ", i);
await delay(controller.signal);
console.log("aaaaaaaa");
console.log(`the number is ${i}`);
}
};
cancelJob();
.as-console-wrapper {
max-height: 100% !important;
}
这也只会延迟循环一次,因为我们将相同的信号传递给所有 delay
函数。最初我是在循环中创建的,如下所示:
const delay = (signal) => {
let done = false;
return new Promise((resolve) => {
tick();
function tick() {
// Cancelled?
if (signal.aborted) {
// Yes, fulfill the promise with null
console.log("Fulfilling with null");
resolve(null);
} else {
// No, wait another second
console.log("Waiting 1000 and checking again");
setTimeout(tick, 1000);
}
}
});
};
const cancelJob = async () => {
for (let i = 6; i > 0; i--) {
const controller = new AbortController();
setTimeout(() => {
console.log("Cancelling");
controller.abort();
}, 4000);
console.log("inside for ", i);
await delay(controller.signal);
console.log("aaaaaaaa");
console.log(`the number is ${i}`);
}
};
cancelJob();
.as-console-wrapper {
max-height: 100% !important;
}
...但后来我看到了另一个答案的“整个循环”评论。
注意:通常当您有一个带有取消功能的异步进程时,它会以特定于取消的拒绝原因拒绝取消,而不是完成,但您明白了。
不需要递归。相反,您可以:
- 使用
setInteval
每秒检查你的状态。
- 当条件正确时,您需要解决承诺。
- 使用
clearInterval
.
let cancelDownload = true
const delay = () => {
let intervalId;
return new Promise((resolve, reject) => {
const check = () => {
if (cancelDownload === true){
console.log('Delaying...');
} else {
clearInterval(intervalId);
resolve();
}
}
//check immediately
check();
//then check every second afterwards
intervalId = setInterval(check, 1000);
});
};
const cancelJob = async() => {
for(let i = 6; i>0; i--){
console.log('inside for ',i);
setTimeout(()=>{
cancelDownload = false
},4000)
await delay()
console.log('aaaaaaaa');
console.log(`the number is ${i}`);
}
}
cancelJob()
这可以按以下方式概括一点 - 不是对条件进行硬编码,而是将其作为回调提供。然后你可以有一个 delay
函数而不使用全局变量,它可以等待不同的事情,而不仅仅是一个变量。
const delayWhile = shouldWait => () => {
let intervalId;
return new Promise((resolve, reject) => {
const check = () => {
if (shouldWait()){
console.log('Delaying...');
} else {
clearInterval(intervalId);
resolve();
}
}
//check immediately
check();
//then check every second afterwards
intervalId = setInterval(check, 1000);
});
};
const cancelJob = async() => {
let cancelDownload = true;
const delay = delayWhile(() => cancelDownload);
for(let i = 6; i>0; i--){
console.log('inside for ',i);
setTimeout(()=>{
cancelDownload = false
},4000)
await delay()
console.log('aaaaaaaa');
console.log(`the number is ${i}`);
}
}
cancelJob()
可取消承诺的一种方法是为承诺创建者函数提供一个可写参数,如 cancelToken
。 promise 创建者使用回调填充此参数,调用时会取消此特定 promise。这通常会导致更简单和更线性的代码。
let pause = () => new Promise(r => setTimeout(r, 100));
async function job(name, steps, cancelToken) {
let cancelled = false;
cancelToken.callback = () => cancelled = true;
for (let step = 1; step <= steps; step++) {
console.log(name, step);
await pause();
if (cancelled) {
console.log('CANCELLED');
break;
}
}
}
async function main() {
let cancelToken = {};
let job1 = job('ok', 5, cancelToken);
await job1;
let job2 = job('bad', 5, cancelToken);
setTimeout(cancelToken.callback, 100);
await job2;
}
main()
作为一般提示,最好尽可能避免 new Promise
和回调。请注意,在此示例中,Promise
和 setTimeout
仅用于测试目的(模拟实际工作),一旦我们将 pause
替换为更有用的内容,其余代码将不会更改,例如fetch
.
let cancelDownload = true
const delay = () => {
return new Promise(() => {
if (cancelDownload === true){
setTimeout(() => {
console.log('Delaying...');
delay();
}, 1000);
}
else return null;
});
};
const cancelJob = async() => {
for(let i = 6; i>0; i--){
console.log('inside for ',i);
setTimeout(()=>{
cancelDownload = false
},4000)
await delay()
console.log('aaaaaaaa');
console.log(`the number is ${i}`);
}
}
cancelJob()
我正在尝试编写一个延迟函数,一旦满足条件,延迟就会被删除并恢复所有代码,但似乎一旦延迟完成,代码就会退出而不执行最后两个控制台日志
你传递给 promise 构造函数(promise 执行器 函数)的函数被调用时带有两个参数:一个用于解析 promise 的函数和一个用于拒绝它的函数(通常称为 resolve
和 reject
)。当您的异步工作完成时,您的代码应该调用这些函数之一。
你的不是,所以承诺永远不会兑现,你的代码永远等待 await
。
但还有其他问题:
1。如果您再次调用 delay
,它会创建一个 new 承诺。您使用 await
的代码只能访问第一个承诺,而不能访问那些递归调用创建的承诺。根本没有任何理由在这里使用递归。
2。对该函数的所有调用共享 相同的 标志。因此,如果我们通过不履行承诺来解决问题,循环会等待,但只会等待一次:
let cancelDownload = false;
const delay = () => {
return new Promise((resolve) => {
tick();
function tick() {
// Cancelled?
if (cancelDownload) {
// Yes, fulfill the promise
console.log("Flag is set, fulfilling");
resolve(null);
} else {
// No, wait another second
console.log("waiting 1000 and checking again");
setTimeout(tick, 1000);
}
}
});
};
const cancelJob = async () => {
setTimeout(() => {
console.log("Setting cancelDownload to true");
cancelDownload = true;
}, 4000);
for (let i = 6; i > 0; i--) {
console.log("inside for ", i);
await delay();
console.log("aaaaaaaa");
console.log(`the number is ${i}`);
}
};
cancelJob();
.as-console-wrapper {
max-height: 100% !important;
}
不过,我可能会误认为第二个是您的用例的问题,因为在对现已删除的答案的评论中,您说您希望循环只等待一次(“整个循环”而不是不仅仅是一次迭代)。
如果你想要一个轮询标志的函数(我不推荐它,轮询通常不是最佳实践,虽然有时你无法避免它)并在设置时履行承诺,你可以使用 AbortController
:
const delay = (signal) => {
let done = false;
return new Promise((resolve) => {
tick();
function tick() {
// Cancelled?
if (signal.aborted) {
// Yes, fulfill the promise with null
console.log("Fulfilling with null");
resolve(null);
} else {
// No, wait another second
console.log("Waiting 1000 and checking again");
setTimeout(tick, 1000);
}
}
});
};
const cancelJob = async () => {
const controller = new AbortController();
setTimeout(() => {
console.log("Cancelling");
controller.abort();
}, 4000);
for (let i = 6; i > 0; i--) {
console.log("inside for ", i);
await delay(controller.signal);
console.log("aaaaaaaa");
console.log(`the number is ${i}`);
}
};
cancelJob();
.as-console-wrapper {
max-height: 100% !important;
}
这也只会延迟循环一次,因为我们将相同的信号传递给所有 delay
函数。最初我是在循环中创建的,如下所示:
const delay = (signal) => {
let done = false;
return new Promise((resolve) => {
tick();
function tick() {
// Cancelled?
if (signal.aborted) {
// Yes, fulfill the promise with null
console.log("Fulfilling with null");
resolve(null);
} else {
// No, wait another second
console.log("Waiting 1000 and checking again");
setTimeout(tick, 1000);
}
}
});
};
const cancelJob = async () => {
for (let i = 6; i > 0; i--) {
const controller = new AbortController();
setTimeout(() => {
console.log("Cancelling");
controller.abort();
}, 4000);
console.log("inside for ", i);
await delay(controller.signal);
console.log("aaaaaaaa");
console.log(`the number is ${i}`);
}
};
cancelJob();
.as-console-wrapper {
max-height: 100% !important;
}
...但后来我看到了另一个答案的“整个循环”评论。
注意:通常当您有一个带有取消功能的异步进程时,它会以特定于取消的拒绝原因拒绝取消,而不是完成,但您明白了。
不需要递归。相反,您可以:
- 使用
setInteval
每秒检查你的状态。 - 当条件正确时,您需要解决承诺。
- 使用
clearInterval
.
let cancelDownload = true
const delay = () => {
let intervalId;
return new Promise((resolve, reject) => {
const check = () => {
if (cancelDownload === true){
console.log('Delaying...');
} else {
clearInterval(intervalId);
resolve();
}
}
//check immediately
check();
//then check every second afterwards
intervalId = setInterval(check, 1000);
});
};
const cancelJob = async() => {
for(let i = 6; i>0; i--){
console.log('inside for ',i);
setTimeout(()=>{
cancelDownload = false
},4000)
await delay()
console.log('aaaaaaaa');
console.log(`the number is ${i}`);
}
}
cancelJob()
这可以按以下方式概括一点 - 不是对条件进行硬编码,而是将其作为回调提供。然后你可以有一个 delay
函数而不使用全局变量,它可以等待不同的事情,而不仅仅是一个变量。
const delayWhile = shouldWait => () => {
let intervalId;
return new Promise((resolve, reject) => {
const check = () => {
if (shouldWait()){
console.log('Delaying...');
} else {
clearInterval(intervalId);
resolve();
}
}
//check immediately
check();
//then check every second afterwards
intervalId = setInterval(check, 1000);
});
};
const cancelJob = async() => {
let cancelDownload = true;
const delay = delayWhile(() => cancelDownload);
for(let i = 6; i>0; i--){
console.log('inside for ',i);
setTimeout(()=>{
cancelDownload = false
},4000)
await delay()
console.log('aaaaaaaa');
console.log(`the number is ${i}`);
}
}
cancelJob()
可取消承诺的一种方法是为承诺创建者函数提供一个可写参数,如 cancelToken
。 promise 创建者使用回调填充此参数,调用时会取消此特定 promise。这通常会导致更简单和更线性的代码。
let pause = () => new Promise(r => setTimeout(r, 100));
async function job(name, steps, cancelToken) {
let cancelled = false;
cancelToken.callback = () => cancelled = true;
for (let step = 1; step <= steps; step++) {
console.log(name, step);
await pause();
if (cancelled) {
console.log('CANCELLED');
break;
}
}
}
async function main() {
let cancelToken = {};
let job1 = job('ok', 5, cancelToken);
await job1;
let job2 = job('bad', 5, cancelToken);
setTimeout(cancelToken.callback, 100);
await job2;
}
main()
作为一般提示,最好尽可能避免 new Promise
和回调。请注意,在此示例中,Promise
和 setTimeout
仅用于测试目的(模拟实际工作),一旦我们将 pause
替换为更有用的内容,其余代码将不会更改,例如fetch
.