在使用
AbortController
时,我没有找到停止执行 Promise 的直接方法。让我解释一下。
为了在 timeout 参数定义的时间限制内等待异步函数 _aget_Cards,我创建了两个 Promise 并按照下面的代码块对它们进行竞赛。
async function xxx (userCN :string, timeout :number) :Promise<CardDetailsWithCCBal[]|unknown> {
const controller = new AbortController()
const raced_promise = Promise.race ([
new Promise ((resolve, reject) => {
_aget_Cards (resolve, reject, userCN, controller)
}),
new Promise ( (resolve, reject) => {
setTimeout ( () => reject('CONTROLLER_TIMEOUT'), timeout)
})
])
raced_promise.catch((error) => {
console.log ('Error on raced promise detected')
if (error === 'CONTROLLER_TIMEOUT') {
console.log ('Detected error is a CONTROLLER_TIMEOUT ... Sending "abort" signal to controller for _aget_Cards')
controller.abort()
}
})
return raced_promise
}
您会注意到,如果时间限制 Promise 提前完成,我会使用
reject('CONTROLLER_TIMEOUT')
引发特定的“CONTROLLER_TIMEOUT”错误。在处理该特定错误时,我使用 controller.abort()
在 AbortController 上调用 abort()。
回调
resolve
、reject
和 AbortController 实例 controller
作为第一个 Promise _aget_Cards (resolve, reject, userCN, controller)
中的参数传递。
在
_aget_Cards函数的
controller.signal.addEventListener
中,我设置了 doAbort = true
,如下面的代码块所示。
async function _aget_Cards (resolve :(value :unknown) => void, reject :(reason ?:any) => void, userCN :string, controller :AbortController) {
let doAbort = false
controller.signal.addEventListener ("abort", () => {
console.log ('Controller received "abort" signal ... aborting ...')
doAbort = true
})
let start_time = Date.now()
if (doAbort) return
const cardDetails_lists :CardDetails[][] = await Promise.all ( [
aget_CardDetails_List (userCN, true), // Get Primary cards
aget_CardDetails_List (userCN, false) // Get Secondary cards
])
console.log(`List of Primary and Secondary Cards obtained in ${(Date.now()-start_time)/1000}s`)
start_time = Date.now()
if (doAbort) return
const cardDetails_list :CardDetails[] = [...cardDetails_lists[0], ...cardDetails_lists[1]]
cardDetails_list.sort ( (a, b) => {
let a_sortVal :string = `${a.cardType}${a.cardNo}`
let b_sortVal :string = `${b.cardType}${b.cardNo}`
return (a_sortVal < b_sortVal) ? -1 : (a_sortVal > b_sortVal) ? 1 : 0
})
if (doAbort) return
...
您会注意到,我已经在多个地方使用
if (doAbort) return
来中止执行。当检测到中止时,我没有找到强制返回的直接方法。
用
reject
内的 controller.signal.addEventListener
拒绝承诺不会影响 _aget_Cards 的执行。也不会引发错误。
非常感谢您的帮助。
我已经在不止一个地方使用
来中止执行。当检测到中止时,我没有找到强制返回的直接方法。if (doAbort) return
是的,这并不像通过
async function
执行注册中止信号那么简单,以便在信号发出时中止执行(通常在 await
期间)。 JavaScript 没有这样的功能:-/
手动测试意味着您可以控制控制流可能中止的地方。就你而言,你是例如在排序之前和之后进行测试,以便排序本身不会被“中断”——它要么执行全部,要么不执行。
但是,您可以大大简化代码:
Promise.race
与在一段时间后拒绝的承诺一起使用。只需在一定时间后中止controller
AbortController
并自己执行 setTimeout
和 controller.abort()
,而是使用 AbortSignal.timeout()
辅助函数async function
作为执行者传递给new Promise
!只需调用该函数,您就已经得到了承诺,并且您不必在正确的时间调用 resolve()
/reject()
AbortSignal
传递给应该停止的函数,而不是整个 AbortController
。您将控制器传递给应该能够停止其他的函数。doAbort
。参考一下signal.aborted
if (signal.aborted) return
,而是调用 signal.throwIfAborted()
,这将抛出中止原因并使其作为异常在调用堆栈中传播所以你的代码就变成了
async function xxx(userCN: string, timeout: number): Promise<CardDetailsWithCCBal[]|unknown> {
const signal = AbortSignal.timeout(timeout)
return _aget_Cards(userCN, signal).catch(error => {
console.log ('Error on promise detected')
if (error === signal.reason) {
console.log ('Detected error is a CONTROLLER_TIMEOUT...')
}
throw e
})
}
async function _aget_Cards (userCN: string, signal: AbortSignal) {
let start_time = Date.now()
signal.throwIfAborted()
const cardDetails_lists: CardDetails[][] = await Promise.all([
aget_CardDetails_List(userCN, true), // Get Primary cards
aget_CardDetails_List(userCN, false) // Get Secondary cards
])
console.log(`List of Primary and Secondary Cards obtained in ${(Date.now()-start_time)/1000}s`)
start_time = Date.now()
signal.throwIfAborted()
const cardDetails_list: cardDetails_lists.flat().sort((a, b) => {
let a_sortVal: string = `${a.cardType}${a.cardNo}`
let b_sortVal: string = `${b.cardType}${b.cardNo}`
return (a_sortVal < b_sortVal) ? -1 : (a_sortVal > b_sortVal) ? 1 : 0
})
signal.throwIfAborted()
…
}