为什么我的 "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
应该只忽略那些实际上期待你取消的回调的取消错误。
我一直认为取消承诺是一种使用高阶函数的透明方式。我想到了这个:
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
应该只忽略那些实际上期待你取消的回调的取消错误。