在 JavaScript 中,如何停止执行丢失 Promise.race 的 Promise?

问题描述 投票:0回答:1

在使用

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 的执行。也不会引发错误。

非常感谢您的帮助。

javascript asynchronous settimeout es6-promise abortcontroller
1个回答
0
投票

我已经在不止一个地方使用

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()
    …
}
© www.soinside.com 2019 - 2024. All rights reserved.