Let's say you're working on an old AngularJS 1.x codebase and you want to use async / await. These keywords don't play nice with AngularJS. Once these promises are fulfilled, you have to wrap them in $q
(or run $apply()
, etc.) in order to loop them into angular's digest cycle. This sucks.
We use babel to convert async / await to generators, then wrap them in script that resolves the genrate with $q
. See https://labs.magnet.me/nerds/2015/11/16/async-await-in-angularjs.html for more details.
The only caveat is that we can only call await
after angular bootstrap. You can still use Promise.then
.
This is a quick and dirty proof of concept. It works great, and it should be relatively easy to improve it.
- Create a file called
ag-await.js
:
/* eslint-disable no-undef */
// Adapted from: https://labs.magnet.me/nerds/2015/11/16/async-await-in-angularjs.html
// Babel will convert "await" to a generator, and then wrap the generator with this function
// This only works if we use await after the app has been bootstrapped
//
// This is intentionally ES5, because it's not clear if this will go through babel since it's
// a babel plugin
var $injector;
function getInjector() {
if ($injector) return $injector;
try {
if (!window.angular) throw new Error('Angular is not available on window!');
$injector = window.angular.element(document).injector();
if (!$injector) throw new Error('Could not get angular injector for document.');
return $injector;
} catch (error) {
console.error(
[
'This error is probably because you have an async / await call before angular bootstrap.',
'Use the stack trace to find out where the issue is, and re-organize your code such that the',
'await is done after angular bootstrap.',
].join('\n'),
);
throw error;
}
}
module.exports.$await = generator => {
return function () {
var args = arguments;
var self = this;
$injector = getInjector();
var $q = $injector.get('$q');
var $rootScope = $injector.get('$rootScope');
return $q.when(
$q(function (resolve, reject) {
var it;
try {
it = generator.apply(self, args);
} catch (e) {
reject(e);
return;
}
function next(val, isError = false) {
var state;
try {
state = isError ? it.throw(val) : it.next(val);
} catch (e) {
$rootScope.$evalAsync();
reject(e);
return;
}
if (state.done) {
$rootScope.$evalAsync();
resolve(state.value);
} else {
$q.when(state.value).then(next, function (err) {
next(err, true);
});
}
}
next();
}),
);
};
};
Then in your babel config:
{
plugins: [
[
'@babel/plugin-transform-async-to-generator',
{
module: require.resolve('./ag-await.js'),
method: '$await',
},
],
],
presets: [
[
'@babel/preset-env',
{
...
// these are handled by the plugin
exclude: [
'@babel/plugin-transform-regenerator',
'@babel/plugin-proposal-async-generator-functions',
'@babel/plugin-transform-async-to-generator',
],
},
],
],
}
Discussion (0)