Я возился с идеей отмены обещания - прозрачный способ использования функции более высокого порядка. И я придумал это:
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()
во втором модульном тесте истекает.
Edit
I думаю это как-то связано с тем, что await
обрабатывает then
метод под капотом. Мне просто нужна помощь на данный момент. Я бы хотел, чтобы он был совместим с asyn c await. Вот проходная версия, которая не зависит от 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)
})