Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.8 breaks use of third-party promise libraries #6737

Closed
bytenik opened this issue Jan 29, 2016 · 17 comments
Closed

1.8 breaks use of third-party promise libraries #6737

bytenik opened this issue Jan 29, 2016 · 17 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@bytenik
Copy link

bytenik commented Jan 29, 2016

I'm currently developing in TS 1.7, compiling to ES6, and transpiling with Babel to ES5. I use Angular 1.x for my frontend development currently.

I am able to leverage async functions by returning a custom type, QPromise, for which I've written a definition like this:

declare class QPromise<T> implements angular.IPromise<T> {
    constructor(resolver: (resolve: angular.IQResolveReject<T>, reject: angular.IQResolveReject<any>) => any);
    then<TResult>(successCallback: (promiseValue: T) => angular.IPromise<TResult>|TResult, errorCallback?: (reason: any) => any, notifyCallback?: (state: any) => any): angular.IPromise<TResult>;
    catch<TResult>(onRejected: (reason: any) => angular.IPromise<TResult>|TResult): angular.IPromise<TResult>;
    finally(finallyCallback: () => any): angular.IPromise<T>;
}

I then create a JavaScript file with a definition QPromise:

function QPromise(resolver) {
    var $q = angular.injector(['ng']).get("$q");
    return new $q(resolver);
};

While less than elegant, this has been an acceptable shim for now so that I can use async while still using Angular's $q library, which is needed to get Angular digest to occur automatically.

TS 1.8 no longer lets me return a QPromise<T>, complaining: error TS1064: The return type of an async function or method must be the global Promise<T> type.

This all but breaks my ability to work with Angular 1 and still use TS async functions.

@vladima
Copy link
Contributor

vladima commented Jan 29, 2016

pinging @rbuckton

@mhegazy mhegazy added the Bug A bug in TypeScript label Jan 29, 2016
@mhegazy mhegazy added this to the TypeScript 1.8.2 milestone Jan 29, 2016
@bytenik
Copy link
Author

bytenik commented Jan 29, 2016

As an aside, it would be awesome if I didn't have to shim this at all, but rather the compiler could handle newing an instance as well as a concrete type. But it seems like your intent was to move away from that altogether and prefer only the built-in Promise object (which yields my issue above).

@rbuckton
Copy link
Member

As of TypeScript 1.8 we are restricting the allowable set of return type's for an async function to only the global Promise.

Unfortunately, this means that you would need to do one of three things:

  1. Go one step further in your shim implementation and set the global Promise to the shim if it is not defined.
  2. Add a Promise compatible shim library that defines a global Promise.
  3. Add a type alias for Promise to a global scope and redefine __awaiter.

An example of [3] might be:

declare global {
  interface Promise<T> extends PromiseLike<T> {}
}
function __awaiter(thisArg: any, _arguments: any, P: any, generator: Function) {
    return new (P || (P = angular.injector(['ng']).get("$q")))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments)).next());
    });
}

async function foo() {
}

@bytenik
Copy link
Author

bytenik commented Jan 29, 2016

Is there a way to make the ES6 included-by-default Promise go away, so that I can supply my own global Promise shim?

@yortus
Copy link
Contributor

yortus commented Jan 29, 2016

relevant: #4692 (comment)

@bytenik
Copy link
Author

bytenik commented Jan 29, 2016

Yuck. 👎

@bytenik
Copy link
Author

bytenik commented Jan 29, 2016

What's the logic behind the change away from allowing any newable PromiseLike promise type anyway?

@yortus
Copy link
Contributor

yortus commented Jan 30, 2016

@bytenik see #6631 and the issue it references #6068. It's about alignment to the ES7 spec (ES7 async functions cannot return custom promises). Also, using type annotations to direct emit causes other problems.

@yortus
Copy link
Contributor

yortus commented Jan 30, 2016

@bytenik I think it's a good thing that TS follows ES semantics for async functions, but what's still missing is a straightforward way to polyfill Promise.

@yortus
Copy link
Contributor

yortus commented Jan 30, 2016

@bytenik since ES7 async functions always return ES6 promises, and you'd like to use async function syntax but work with custom promises, a forward-compatible approach might be to use a wrapper around your async functions as suggested here.

@bytenik
Copy link
Author

bytenik commented Jan 30, 2016

Based on that logic, I agree. Unfortunately until there's a better way to stick my own promise in, I'm stuck with 1.7.

Does the awaiter suggestion above work if I use the built-in es6 library and don't replace the global Promise? Or will I conflict?

@rbuckton
Copy link
Member

Keep in mind that the __awaiter helper only exists to downlevel async functions to a version of the ES standard that does not support them. While its perfectly feasible to write a custom __awaiter helper, once async functions are part of an official specification we will emit them natively when targeting that specification version. Any changes to __awaiter wouldn't be reflected when emitting async functions natively, nor would any changes to the global Promise.

Changing __awaiter is a stop-gap solution to fill in support for async functions in a runtime environment without a global Promise. Another solution is to polyfill a global Promise through a library like es6-shim.

@bytenik
Copy link
Author

bytenik commented Jan 30, 2016

@rbuckton Absolutely, but its a stop-gap that I can use now. es6-shim won't help here; I have es6 compilation already, and I need to use $q rather than es6 promises.

@yortus
Copy link
Contributor

yortus commented Jan 30, 2016

@bytenik not sure if you saw my last comment above as we both posted about the same time - it's another approach you could take.

@afaayerhan
Copy link

this is all rubish. native promise are said to be not faster. and many libraries tend to use bluebird promise. I'm using sequelize with async await and this break sequelize as I would be getting unhandled execeptions even when I wrap it in a try catch block. please sometimes it's better flex your decision of going with the spec than to cause large breakage in apps that use async await. only a few libraries you the native promises and many promise implementations mostly align with the spec. so the bring your own promise should an option that should be kept. async await jobs is to provide a clean way to write async code. not try to be wrapping it in another callback

@mhegazy
Copy link
Contributor

mhegazy commented Feb 5, 2016

Based on the discussion in #6930, We need to revert the change in release-1.8.

@rbuckton can you send a PR.

@mhegazy mhegazy added the Fixed A PR has been merged for this issue label Feb 11, 2016
@mhegazy mhegazy closed this as completed Feb 11, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Feb 11, 2016

fixed by #6967

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

6 participants