Currently (March 2019) IE11 still needs to be supported, and this requires async/await to be transpiled. Babel's built-in path is to transform async-to-generator and use regenerator-runtime, which adds a significant bundle size penalty.
It would be preferable to transpile async/await to a Promise chain. There are several projects:
- Kneden which is incomplete and seems abandoned
- rpetrich's transform turns into a callback pyramid and not a promise chain
- fast-async which focuses on runtime performance and not bundle size.
It does not seem to have significant bundle size advantages over the regenerator approach.
- A proposal to upstream this into babel proper seems stuck on disagreements over relying on an external non-babel library to do the transform.
This is a set of example transforms that produce compact code, and might form the basis for one more attempt at this.
async function foo() {
const m = await bar();
const n = await baz();
return 3 + m / n;
}
function foo() {
let m;
let n;
return Promise.resolve(bar())
.then(_m => {
m = _m;
return baz();
})
.then(_n => {
n = _m;
return 3 + m / n
});
}
// -----------------------
async function foo() {
let x = 0;
while (true) {
const y = await bar();
if (y === 0) return 50;
if (y === 1) throw Error();
if (y === 2) continue;
if (y === 3) break;
x += y;
}
return x;
}
const RETURN = Symbol();
function foo() {
let x = 0;
const _loop = () => {
let y;
return Promise.resolve(bar())
.then(_y => {
y = _y;
if (y === 0) throw { [RETURN]: 50 };
if (y === 1) throw Error();
if (y === 2) return _loop();
if (y === 3) return;
x += y;
return _loop();
})
.catch(_e => {
// Return turner.
if (typeof _e === 'object' && _e && _e[RETURN]) return _e[RETURN];
throw _e;
})
}
return _loop()
.then(() => { return x; });
}
// -------------------------------
async function foo() {
try {
const x = await bar();
if (x === 1) return x + 1;
} catch (e) {
await baz(e);
if (e === 1) throw e;
return 42;
} finally {
await bat();
}
}
function foo() {
let x;
let e;
return Promise.resolve(bar())
.then(_x => {
x = _x;
// We throw the result so we can skip the catch's .then() chain
if (x === 1) throw { [RETURN]: x + 1 };
throw { [FINALLY] }; // This ensures finally is called
})
.catch(_e => {
if (typeof _e === 'object' && _e && typeof _e[RETURN]) throw _e;
e = _e;
return baz(e);
})
.then(() => {
// Continuing catch() block
if (e === 1) throw e;
throw { [RETURN]: 42 };
})
.then(() => { throw { [FINALLY] }; }) // This ensures finally is called
.catch(() => {
// Returned already? Pass through to the return-turning catch.
if (typeof _e === 'object' && _e && typeof _e[RETURN]) throw _e;
// This is "finally"
return bat();
})
.then(() => {
})
.catch(_e => {
// Return turner.
if (typeof _e === 'object' && _e && _e[RETURN]) return _e[RETURN];
throw _e;
});
}
// ---------------------------------------------