...

/

Cancelable Async Functions with Generators

Cancelable Async Functions with Generators

Learn how to use generators to create cancelable async functions.

We'll cover the following...

The cancelable wrapper function is a big step ahead compared to embedding the cancelation logic directly in our code. However, it’s still not ideal for two reasons: it’s error prone (what if we forget to wrap one function?) and it still affects the readability of our code, which makes it not ideal for implementing cancelable asynchronous operations that are already large and complex.

Using generators

An even neater solution involves the use of generators. Generators act as a means to implement iterators. However, they’re a very versatile tool and can be used to implement all sorts of algorithms. In this case, we’ll be using generators to build a supervisor to control the asynchronous flow of a function. The result will be an asynchronous function that’s transparently cancelable, whose behavior resembles an async function in which the await instruction is replaced by the yield instruction.

Let’s look at how we can implement this cancelable function using generators (the createAsyncCancelable.js file).

Press + to interact
import { CancelError } from './cancelError.js'
export function createAsyncCancelable (generatorFunction) { //(1)
return function asyncCancelable (...args) {
const generatorObject = generatorFunction(...args) //(3)
let cancelRequested = false
function cancel () {
cancelRequested = true
}
const promise = new Promise((resolve, reject) => {
async function nextStep (prevResult) { //(4)
if (cancelRequested) {
return reject(new CancelError())
}
if (prevResult.done) {
return resolve(prevResult.value)
}
try { //(5)
nextStep(generatorObject.next(await prevResult.value))
} catch (err) {
try { //(6)
nextStep(generatorObject.throw(err))
} catch (err2) {
reject(err2)
}
}
}
nextStep({})
})
return { promise, cancel } //(2)
}
}

The createAsyncCancelable() ...