为什么我的 "thenable" 函数超时?

Why does my "thenable" function time out?

我一直认为取消承诺是一种使用高阶函数的透明方式。我想到了这个:

export const fnGetter = state => fn => (...args) => {
  if (!state.canceled) return fn(...args)
  return Promise.resolve()
}

export const cancelable = (promise, state = {}) => {
  const getFn = fnGetter(state)

  return {
    then: fn => cancelable(promise.then(getFn(fn)), state),
    catch: fn => cancelable(promise.catch(getFn(fn)), state),
    cancel: () => {
      state.canceled = true
    }
  }
}

export const withCancel = promiseReturningFn => (...args) =>
  cancelable(promiseReturningFn(...args))

这里有一些单元测试,我在其中验证我想要的行为。

const delay = withCancel(ms => new Promise(run => setTimeout(run, ms)))

test('works like normal promise when not canceled', async () => {
  const first = jest.fn()
  const second = jest.fn()

  await delay(1000).then(first).then(second)

  expect(first).toHaveBeenCalledTimes(1)
  expect(second).toHaveBeenCalledTimes(1)
})

test('when ignored, does not call callbacks', async () => {
  const first = jest.fn()
  const second = jest.fn()

  const promise = delay(1000).then(first).then(second)
  promise.cancel()

  await promise

  expect(first).not.toHaveBeenCalled()
  expect(second).not.toHaveBeenCalled()
})

我不明白为什么第一个测试通过了,但是在第二个单元测试中调用 .cancel() 使它超时。

编辑

认为 它与 await 在幕后处理 then 方法的方式有关。我现在只需要帮助。我希望它与异步等待兼容。这是一个不依赖于 await.

的传递版本
test('when ignored, does not call callbacks', async () => {
  const first = jest.fn()
  const second = jest.fn()

  const promise = delay(1000).then(first).then(second)
  promise.cancel()

  setTimeout(() => {
    expect(first).not.toHaveBeenCalled()
    expect(second).not.toHaveBeenCalled()
  }, 2000)
})

我想我知道发生了什么。从阅读中可以看出,await 只是采用后续代码,将其包装到一个函数中,然后将该函数传递给正在等待的 then 方法。如果是这种情况,那么我的代码正在运行并且 expect 语句永远不会 运行 因为我的承诺(正在等待的那个)被取消了。这就是 运行 使用 setTimeout 进行测试的原因。

这是我所指功能的基本示例。

const func = async () => {
  await { then: () => console.log("test") }
  console.log("after")
}

上面的代码打印 "test" 而从不打印 "after" 因为 console.log("after") 被包装在一个函数中并传递给对象的 then 方法,而该方法从不调用它。

一个问题是 .

至于超时原因:您正在使用 await promise,但 await 确实使用了 then,并且您的 promise 已被取消,因此它永远不会调用其回调.取消的承诺应该调用 onreject 回调,然后你的 fnGetter 应该只忽略那些实际上期待你取消的回调的取消错误。