为什么 Promise.race() 会履行未履行的承诺?
Why does Promise.race() fulfill with an unfulfilled promise?
我正在 运行在 Puppeteer 的页面上设置一系列函数。如果其中一个函数抛出异常,我会重试整个序列几次,因为我使用的系统不可靠。它比处理所有可能的异常更容易。
有时这些步骤之一会挂起而不是抛出,所以我想有一个安全超时,然后重试就好像发生了异常。
如果我不在下面 asyncCallWithTimeout()
中包装我的序列,一切都会正常工作并解析为我想要的(一个数组)。但是,当我确实使用该函数时,我留下了一个未解析的函数,该函数作为 asyncPromise
.
传递
对于我来说,我无法弄清楚为什么 Promise.race()
会以 未解决 asyncPromise
来解决,我通过。我错过了什么?
这是在 运行 太长时使其他异步函数超时的函数:
async function asyncCallWithTimeout(asyncPromise, timeLimit) {
let timeoutHandle;
const timeoutPromise = new Promise((_resolve, reject) => {
timeoutHandle = setTimeout(
() => reject(new Error('Async call timeout limit reached')),
timeLimit
);
});
return Promise.race([asyncPromise, timeoutPromise]).then((result) => {
clearTimeout(timeoutHandle); // .then() looks to be executed every time even if timeout is set to 1 ms
return result;
});
};
const timer = ms => new Promise(r => setTimeout(r, ms));
const shouldResolve = asyncCallWithTimeout(timer(1000), 2000).then(console.log).catch(console.error);
const shouldReject = asyncCallWithTimeout(timer(2000), 1000).then(console.log).catch(console.error);
这是相关的重试函数:
async function retry(promiseFactory, retryCount) {
try {
return await promiseFactory();
} catch (error) {
if (retryCount <= 0) {
throw error;
}
return await retry(promiseFactory, retryCount - 1);
}
};
我是这样使用的:
async function checkT(browser, url) {
const tUrl = `https://example.com/?url=${url}`;
return retry(async () => {
const browserPage = await browser.newPage();
try {
return asyncCallWithTimeout(async () => { // works if this is commented
await browserPage.goto(tUrl);
await doSomething(browserPage);
await waitForSomething(browserPage);
const results = await getResults(browserPage);
await browserPage.close();
return results;
}, 10000); // works if this is commented
} catch (error) {
await browserPage.close();
throw error;
}
}, 3);
};
然后我在其他地方这样调用它(使用不相关的 p-queue):
for (const page of pages) {
queue.add(() => checkT(browser, page.url)
.then(async (results) => {
// results should be an array but is a function and not iterable hence fails below
for (const result of results.data) {
// do something with the result
}
})
);
};
你正在传递一个函数作为 asyncCallWithTimeout
的第一个参数,但我假设你想传递一个给定参数名称和你如何使用它的承诺。
当您调用 Promise.race 时,asyncPromise 仍然是一个函数:
Promise.race([asyncPromise, timeoutPromise])
要使其正常工作,您应该调用它:
return asyncCallWithTimeout((async () => {
await browserPage.goto(tUrl);
await doSomething(browserPage);
await waitForSomething(browserPage);
const results = await getResults(browserPage);
await browserPage.close();
return results;
})(), 10000);
// ^
// |
// You arrow function called here,
// so this should return a promise
// to the first argument of `asyncCallWithTimeout`
注意函数是用括号括起来的,然后在这里调用。显然,这不是调用该函数的唯一方法,您可以为其命名并调用它,然后传入返回的 promise 变量,但我希望您能理解我的意思。
我正在 运行在 Puppeteer 的页面上设置一系列函数。如果其中一个函数抛出异常,我会重试整个序列几次,因为我使用的系统不可靠。它比处理所有可能的异常更容易。
有时这些步骤之一会挂起而不是抛出,所以我想有一个安全超时,然后重试就好像发生了异常。
如果我不在下面 asyncCallWithTimeout()
中包装我的序列,一切都会正常工作并解析为我想要的(一个数组)。但是,当我确实使用该函数时,我留下了一个未解析的函数,该函数作为 asyncPromise
.
对于我来说,我无法弄清楚为什么 Promise.race()
会以 未解决 asyncPromise
来解决,我通过。我错过了什么?
这是在 运行 太长时使其他异步函数超时的函数:
async function asyncCallWithTimeout(asyncPromise, timeLimit) {
let timeoutHandle;
const timeoutPromise = new Promise((_resolve, reject) => {
timeoutHandle = setTimeout(
() => reject(new Error('Async call timeout limit reached')),
timeLimit
);
});
return Promise.race([asyncPromise, timeoutPromise]).then((result) => {
clearTimeout(timeoutHandle); // .then() looks to be executed every time even if timeout is set to 1 ms
return result;
});
};
const timer = ms => new Promise(r => setTimeout(r, ms));
const shouldResolve = asyncCallWithTimeout(timer(1000), 2000).then(console.log).catch(console.error);
const shouldReject = asyncCallWithTimeout(timer(2000), 1000).then(console.log).catch(console.error);
这是相关的重试函数:
async function retry(promiseFactory, retryCount) {
try {
return await promiseFactory();
} catch (error) {
if (retryCount <= 0) {
throw error;
}
return await retry(promiseFactory, retryCount - 1);
}
};
我是这样使用的:
async function checkT(browser, url) {
const tUrl = `https://example.com/?url=${url}`;
return retry(async () => {
const browserPage = await browser.newPage();
try {
return asyncCallWithTimeout(async () => { // works if this is commented
await browserPage.goto(tUrl);
await doSomething(browserPage);
await waitForSomething(browserPage);
const results = await getResults(browserPage);
await browserPage.close();
return results;
}, 10000); // works if this is commented
} catch (error) {
await browserPage.close();
throw error;
}
}, 3);
};
然后我在其他地方这样调用它(使用不相关的 p-queue):
for (const page of pages) {
queue.add(() => checkT(browser, page.url)
.then(async (results) => {
// results should be an array but is a function and not iterable hence fails below
for (const result of results.data) {
// do something with the result
}
})
);
};
你正在传递一个函数作为 asyncCallWithTimeout
的第一个参数,但我假设你想传递一个给定参数名称和你如何使用它的承诺。
当您调用 Promise.race 时,asyncPromise 仍然是一个函数:
Promise.race([asyncPromise, timeoutPromise])
要使其正常工作,您应该调用它:
return asyncCallWithTimeout((async () => {
await browserPage.goto(tUrl);
await doSomething(browserPage);
await waitForSomething(browserPage);
const results = await getResults(browserPage);
await browserPage.close();
return results;
})(), 10000);
// ^
// |
// You arrow function called here,
// so this should return a promise
// to the first argument of `asyncCallWithTimeout`
注意函数是用括号括起来的,然后在这里调用。显然,这不是调用该函数的唯一方法,您可以为其命名并调用它,然后传入返回的 promise 变量,但我希望您能理解我的意思。