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?

Eran Shabi
  • 14,201
  • 7
  • 30
  • 51
Random
  • 3,807
  • 2
  • 30
  • 49
  • 1
    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 cycle – Random Feb 26 '16 at 14:07
  • There is an open issue about this https://github.com/Microsoft/TypeScript/issues/6122 – thorn0 Jul 24 '18 at 10:42

3 Answers3

15

You can make it work like this:

angular.module('your app')
        .run(['$window', '$q', function($window, $q) {
            $window.Promise = $q;
        }]);
Eran Shabi
  • 14,201
  • 7
  • 30
  • 51
  • 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-promises – Random Jan 25 '17 at 21:49
  • 4
    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. – Judah Gabriel Himango Jul 03 '17 at 19:20
  • 7
    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. – Estus Flask Jul 30 '17 at 19:09
  • @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. – Schoof May 28 '18 at 08:02
  • @Schoof 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://stackoverflow.com/a/45119519/3731501 for instance . A digest should be triggered after async function execution in more or less explicit manner. – Estus Flask May 28 '18 at 08:07
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));
    });
};
Amid
  • 21,508
  • 5
  • 57
  • 54
  • 4
    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 `await` – Random Feb 26 '16 at 11:39
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

Random
  • 3,807
  • 2
  • 30
  • 49
  • Just out of curiosity how does this handle rejects? catch block? – Luis Nov 08 '16 at 12:59
  • Yes. Added an example to the repository – Random Nov 14 '16 at 21:29
  • 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. – Estus Flask Jul 30 '17 at 19:39