Typescript async/await and angular $q service
Asked Answered
C

3

19

New TypeScript async/await feature uses ES6 promises. AngularJS uses $q service promises with slightly different interface.

Is there any way to use TypeScript async/await feature with $q service promises?

Constitutional answered 25/2, 2016 at 13:52 Comment(2)
Yes. The first problem is that I have to convert every promise-response from ng-service to use await against it. The second problem is that ES6-promises generated by await expression don't start angular digest cycleConstitutional
There is an open issue about this github.com/Microsoft/TypeScript/issues/6122Ssm
R
15

You can make it work like this:

angular.module('your app')
        .run(['$window', '$q', function($window, $q) {
            $window.Promise = $q;
        }]);
Regressive answered 24/1, 2017 at 9:52 Comment(5)
Thank you, it's much more neat than my version. I didn't know that $q service can be used as a constructor compatible with es6-promisesConstitutional
This works, and I'm using it in several projects now. But I can't help but shake the feeling that this is naughty. Replacing the global Promise object with the Angular $q service seems...hackish.Gladwin
Replacing native Promise with $q is probably the worst thing the one can ever do to the application. They are fundamentally different implementations that behave differently, this will ruin all third-party code that depends on Promise. The statement about 'hackish' $window is false - it's there not because Angular is incapable of using native window, but because DI is good.Brutus
@estus So what should we do instead? I've been looking all over the web to get async / await working decently with AngularJS but I cannot find a good consensus.Elinoreeliot
@Elinoreeliot By the way, the answer won't work for native async (TypeScript es2017 target) any way, because it relies on internal native implementation and not Promise global. See https://mcmap.net/q/411474/-typescript-async-await-doesnt-update-angularjs-view for instance . A digest should be triggered after async function execution in more or less explicit manner.Brutus
C
5

I do not think you will be able to use them directly. But it should be quite easy to convert q promise into a++ promise, something like this:

function Convert<T>(qPromise): Promise<T> 
{
    return new Promise<T>((resolve, reject) =>
    {
        qPromise.then((result: T) => resolve(result), (e) => reject(e));
    });
};
Chamade answered 25/2, 2016 at 15:14 Comment(1)
The problem is that in this case I have to wrap each ng service that returns $q promises. Also, ES6-promises don't initiate angular digest cycle. So, in this case I have to call $apply after each awaitConstitutional
C
1

Finally I used the following workaround:

declare var __awaiter: Function;
(window as any).__awaiter = __awaiter; // set global __awaiter to avoid declaring default __awaiter in other files
async () => { } // dummy async function to generate __awaiter code for current file

angular.module('ts-awaiter', []).run(['$timeout', ($timeout: ng.ITimeoutService) => {
    function wrap(func: Function) {
        return function () {
            func.apply(this, arguments);
            $timeout(() => { }); // run angular digest
        };
    }

    var oldAwaiter = __awaiter;
    (window as any).__awaiter = (thisArg: any, _arguments: any, P: Function, generator: any) => {
        P = function (executor: Function) {
            return new Promise<any>((resolve, reject) => {
                resolve = wrap(resolve);
                reject = wrap(reject);
                executor(resolve, reject);
            });
        };
        return oldAwaiter(thisArg, _arguments, P, generator);
    };
}]);

Comliper for Typescript 1.8 generates __awaiter function in every file where await operator is used. I replace it with implementation which passes custom Promise constructor which initiates digest cycle after every resolve and reject call. Here is usage example: https://github.com/llRandom/ts-awaiter

Constitutional answered 17/7, 2016 at 11:37 Comment(3)
Just out of curiosity how does this handle rejects? catch block?Homopolar
Yes. Added an example to the repositoryConstitutional
This may result in unneeded digests, and digests are the most common bottleneck for AngularJS app performance. And the solution is not applicable to TypeScript ES2017 target... native async/await is already there.Brutus

© 2022 - 2024 — McMap. All rights reserved.