I use bluebird a lot in my project. It has benefits over standard Promise implementation, such as better api and performance. Alwo bluebird's promises fully backward compatible with standard Promise interface, so, it can be used as replacement of standard Promise.
But, trying to use async / await
functions with Bluebird I got an error about impossibility to use Bluebird as return of async function. Consider the example:
async function readFile(fileName: string): Bluebird<string> {
}
As for now I should explicitly write something like that:
function readFile(fileName: string): Bluebird<string> {
return Bluebird.try(async () => {
})
}
This extra wrapper makes me sad :( While I can force to use bluebird's promise implementation by overloading global.Promise
or hoook my transpiler (babel or somewhat else), I can't use it with typescript :(
Works for me. You're doing something wrong.
@Strate Are you using "target": "es5"
, or "target": "es2015"
In the former case, this works fine:
import Bluebird from 'bluebird';
export async function getString(): Bluebird<string> {
return await 'hello';
}
export async function useString(): Bluebird<void> {
console.log(await getString());
}
In the latter case, the above code will issue an error stating
The return type of an async function or method must be the global Promise
type.
This is required by the ECMAScript spec which mandates that async
function return instances of the global Promise function. You can get around this with by writing the following
// bootstrap.ts
import Bluebird from 'bluebird';
Promise = Promise || Bluebird as any;
// example.ts
export async function getString() {
return await 'hello';
}
export async function useString() {
console.log(await getString());
}
But your code will not be spec compliant if you are targeting a runtime that provides Promises.
This has nothing specifically to do with TypeScript.
Thanks @aluanhaddad for patient explanation! Learn a lot from your comment.
I peruse the TC39's proposal, specifically in the section abstract-async-function-start, where the return behavior is specified, it reads:
If result.[[type]] is normal, then
Perform ! Call(promiseCapability.[[Resolve]], undefined, «undefined»).
After another lengthy search, the promiseCapability.[[Resolve]]
is promise's native resolve, which is defined here
8. Let then be Get(resolution, "then").
9. If then is an abrupt completion, then
a. Return RejectPromise(promise, then.[[value]]).
10. Let thenAction be then.[[value]].
11. If IsCallable(thenAction) is false, then
b. Return FulfillPromise(promise, resolution).
12. Perform EnqueueJob ("PromiseJobs", PromiseResolveThenableJob, «promise, resolution, thenAction»)
If I read the spec correctly, it means every async function will return a native promise. But in the async body, every promise compatible object can be await
ed or return
ed.
Thanks @aluanhaddad for patient explanation! Learn a lot from your comment.
I am pretty sure I learned it from someone else, perhaps a TypeScript team member, but I forget. Regardless, you are most welcome 😉
If I read the spec correctly, it means every async function will return a native promise. But in the async body, every promise compatible object can be awaited or returned.
I believe you are reading that correctly. It does seem to suggest that it is indeed only top level async
bodies which are restricted to returning instances of the global Promise
type.
The behavior of TypeScript's implementations matches your reading.
With "target": es2015"
_TypeScript_
import Bluebird from 'bluebird';
function getValue() { // The return type is inferred as Bluebird<number>.
return Bluebird.resolve(4);
}
async function getValue() { // The return type is inferred as Promise<number>.
return await Bluebird.resolve(4);
}
async function getValue1() { // The return type is also inferred as Promise<number>.
return Bluebird.resolve(4);
}
Interestingly with "target": "es5"
and "lib": "[ "es2015" ]"
we are allowed to mix and match with async
functions returning the result type of propagating their awaitable but only when explicitly specified.
_TypeScript_
import Bluebird from 'bluebird';
// The return type annotation changes the emit. This doesn't happen anywhere else in TypeScript.
// If we do not have a `Promise` declaration in scope, like if we set `"lib": [ "es5" ]`,
// we will get a compiler error unless we specify the return type explicitly.
async function getValue(): Bluebird<number> { // The return type is declared as Bluebird<number>.
return await Bluebird.resolve(4);
}
_ES5.1_
import Bluebird from 'bluebird';
function getValue() {
// And it indeed returns a Bluebird!
return __awaiter(this, void 0, Bluebird, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, Bluebird.resolve(4)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
EDIT:
This interaction between the ambient global declaration and the explicit return type annotation of the async
function is confusing and can be abused in weird ways.
I think I ran into this too - or possibly a related issue - because I wanted to use mocha-typescript
and to have my test suite import my app code, while having my test project be its own typescript project.
This prompted me to add declaration: true
to my src project's tsconfig.json so my test project can consume/reference it.
And my main application's entry point exports an async function as its default export like this:
import * as Promise from 'bluebird';
/* ... all imports and stuff... */
import * as feathers from 'feathers';
const feathersApp = feathers();
const configApp = async (app: feathers.Application) => {
/*... await some stuff.... await more stuff... */
return app;
}
export default configApp(feathersApp); /* ERR with bluebird as Promise (w/ declarations) */
Getting error - which I suppose I could sidestep with the aforementioned wrapper? Or possible a declaration...?
declare var Promise: bluebird<any>;
with import * as bluebird from 'bluebird'
instead of importing bluebird as promise?
node.js: 8.0.0
npm: 5.0.2
tsc: 2.3.4
VSCode: 1.13.0
Both tsconfigs targets es2017
.
Any advice? Thanks for reading 😸
Seems like most people got this sorted out and native Promise
implementations are everywhere now, so I don't think there's any further action warranted from our side. Thanks everyone for being a helping community!
Most helpful comment
@Strate Are you using
"target": "es5"
, or"target": "es2015"
In the former case, this works fine:
In the latter case, the above code will issue an error stating
This is required by the ECMAScript spec which mandates that
async
function return instances of the global Promise function. You can get around this with by writing the followingBut your code will not be spec compliant if you are targeting a runtime that provides Promises.
This has nothing specifically to do with TypeScript.