GeezerVison is showing github.com — Proposal: Async Functions · Issue #1664 · microsoft/TypeScript
Proposal: Async Functions #1664DescriptionAsync Functions
1 Async Functions
This is a spec proposal for the addition of Async Functions (also known as async..await) as a feature of TypeScript.
2 Use Cases
Async Functions allow TypeScript developers to author functions that are expected to invoke an asynchronous operation and await its result without blocking normal execution of the program. This accomplished through the use of an ES6-compatible Promise implementation, and transposition of the function body into a compatible form to resume execution when the awaited asynchronous operation completes.
This is based primarily on the Async Functions strawman proposal for ECMAScript, and C# 5.0 § 10.15 Async Functions.
3 Introduction
3.1 Syntax
An Async Function is a JavaScript Function, Parameterized Arrow Function, Method, or Get Accessor that has been prefixed with the async modifier. This modifier informs the compiler that function body transposition is required, and that the keyword await should be treated as a unary expression instead of an identifier. An Async Function must provide a return type annotation that points to a compatible Promise type. Return type inference can only be used if there is a globally defined, compatible Promise type.
Example:
var p: Promise<number> = /* ... */;
async function fn(): Promise<number> {
var i = await p; // suspend execution until 'p' is settled. 'i' has type "number"
return 1 + i;
}
var a = async (): Promise<number> => 1 + await p; // suspends execution.
var a = async () => 1 + await p; // suspends execution. return type is inferred as "Promise<number>" when compiling with --target ES6
var fe = async function(): Promise<number> {
var i = await p; // suspend execution until 'p' is settled. 'i' has type "number"
return 1 + i;
}
class C {
async m(): Promise<number> {
var i = await p; // suspend execution until 'p' is settled. 'i' has type "number"
return 1 + i;
}
async get p(): Promise<number> {
var i = await p; // suspend execution until 'p' is settled. 'i' has type "number"
return 1 + i;
}
}
3.2 Transformations
To support this feature, the compiler needs to make certain transformations to the function body of an Async Function. The type of transformations performed depends on whether the current compilation target is ES6, or ES5/ES3.
3.2.1 ES6 Transformations
During compilation of an Async Function when targeting ES6, the following transformations are applied:
- The new function body consists of a single return statement whose expression is a new instance of the promise type supplied as the return type of the Async Function.
- The original function body is enclosed in a Generator Function.
- Any
await expressions inside the original function body are transformed into a compatible yield expression.
- As the precedence of
yield is much lower than await, it may be necessary to enclose the yield expression in parenthesis if it is contained in the left-hand-side of a binary expression.
- The Generator Function is executed and the resulting generator is passed as an argument to the
__awaiter helper function.
- The result of the
__awaiter helper function is passed as an argument to the promise resolve callback.
Example:
// async.ts
var p0: Promise<number> = /* ... */;
var p1: Promise<number> = /* ... */;
async function fn() {
var i = await p0;
return await p1 + i;
}
// async.js
var __awaiter = /* ... */;
function fn() {
return new Promise(function (_resolve) {
_resolve(__awaiter(function* () {
var i = yield p0;
return (yield p1) + i;
}()));
});
}
3.2.2 ES5/ES3 Transformations
As ES5 and earlier do not support Generator Functions, a more complex transformation is required. To support this transformation, __generator helper function will be also emitted along with the __awaiter helper function, and a much more comprehensive set of transformations would be applied:
- The new function body consists of a single return statement whose expression is a new instance of the promise type supplied as the return type of the Async Function.
- The original function body is enclosed in a function expression with a single parameter that is passed as an argument to the
__generator helper function.
- All hoisted declarations (variable declarations and function declarations) in the original function body are extracted and added to the top of the new function body.
- The original function body is rewritten into a series of case clauses in a switch statement.
- Each statement in the function body that contains an
await expression is rewritten into flattened set of instructions that are interpreted by the __generator helper function.
- Temporary locals are generated to hold onto the values for partially-applied expressions to preserve side effects expected in the original source.
- Logical binary expressions that contain an
await expression are rewritten to preserve shortcutting.
- Assignment expressions that contain an
await are rewritten to store portions left-hand side of the assignment in temporary locals to preserve side effects.
- Call expressions that contain an
await in the argument list are rewritten to store the callee and this argument, and to instead call the call method of the callee.
- New expressions that contain an
await in the argument list are rewritten to preserve side effects.
- Array literal expressions that contain an
await in the element list are rewritten to preserve side effects.
- Object literal expressions that contain an
await in the element list are rewritten to preserve side effects.
- try statements that contain an await in the try block, catch clause, or finally block are rewritten and tracked as a protected region by the
__generator helper function.
- The variable of a
catch clause is renamed to a unique identifier and all instances of the symbol are renamed to preserve the block scope behavior of a catch variable.
break and continue statements whose target is a statement that contains an await expression are rewritten to return an instruction interpreted by the __generator helper function.
return statements are rewritten to return an instruction interpreted by the __generator helper function.
for..in statements that contain an await expression are rewritten to capture the enumerable keys of the expression into a temporary array, to allow the iteration to be resumed when control is returned to the function following an await expression.
- Labeled statements that contain an
await expression are rewritten.
await expressions are rewritten to return an instruction interpreted by the __generator helper function.
Example:
// async.ts
var p0: Promise<number> = /* ... */;
var p1: Promise<number> = /* ... */;
async function fn() {
var i = await p0;
return await p1 + i;
}
// async.js
var __awaiter = /* ... */;
var __generator = /* ... */;
function fn() {
var i;
return new Promise(function (_resolve) {
resolve(__awaiter(__generator(function (_state) {
switch (_state.label) {
case 0: return [3 /*yield*/, p0];
case 1:
i = _state.sent;
return [3 /*yield*/, p1];
case 2:
return [2 /*return*/, _state.sent + i];
}
}));
});
}
The following is an example of an async function that contains a try statement:
// async.ts
var p0: Promise<number> = /* ... */;
var p1: Promise<number> = /* ... */;
async function fn() {
try {
await p0;
}
catch (e) {
alert(e.message);
}
finally {
await p1;
}
}
// async.js
var __awaiter = /* ... */;
var __generator = /* .. */;
function fn() {
var i;
return new Promise(function (_resolve) {
resolve(__awaiter(__generator(function (_state) {
switch (_state.label) {
case 0:
_state.trys = [];
_state.label = 1;
case 1:
_state.trys.push([1, 3, 4, 6]);
return [3 /*yield*/, p0];
case 2:
return [5 /*break*/, 6];
case 3:
_a = _state.error;
alert(_a.message);
return [5 /*break*/, 6];
case 4:
return [3 /*yield*/, p1];
case 5:
return [6 /*endfinally*/];
case 6:
return [2 /*return*/];
}
}));
});
var _a;
}
As a result of these transformations, the JavaScript output for an Async Function can look quite different than the original source. When debugging the JavaScript output for an Async Function it would be advisable to use a Source Map generated using the --sourceMap option for the compiler.
4 Promise
Async Functions require a compatible Promise abstraction to operate properly. A compatible implementation implements the following interfaces, which are to be added to the core library declarations (lib.d.ts):
interface IPromiseConstructor<T> {
new (init: (resolve: (value: T | IPromise<T>) => void, reject: (reason: any) => void) => void): IPromise<T>;
}
interface IPromise<T> {
then<TResult>(onfulfilled: (value: T) => TResult | IPromise<TResult>, onrejected: (reason: any) => TResult | IPromise<TResult>): IPromise<TResult>;
}
The following libraries contain compatible Promise implementations:
A Grammar
A.1 Types
CallSignature [Await,AsyncParameter] :
TypeParameters opt ( ParameterList [?Await,?AsyncParameter]opt ) TypeAnnotation opt*
ParameterList_ [Await,AsyncParameter] *:_
RequiredParameterList [?Await]
OptionalParameterList [?Await,?AsyncParameter]
RestParameter [?Await]
RequiredParameterList [?Await] , OptionalParameterList [?Await,?AsyncParameter]
RequiredParameterList [?Await] , RestParameter [?Await]
OptionalParameterList [?Await,?AsyncParameter] , RestParameter [?Await]
RequiredParameterList [?Await] , OptionalParameterList [?Await,?AsyncParameter] , RestParameter [?Await]
RequiredParameterList [Await] :
RequiredParameter [?Await]
RequiredParameterList [?Await] , RequiredParameter [?Await]
RequiredParameter [Await] :
AccessibilityModifier opt BindingIdentifier [?Await] TypeAnnotation opt
Identifier : StringLiteral
OptionalParameterList [Await,AsyncParameter] :
OptionalParameter [?Await,?AsyncParameter]
OptionalParameterList [?Await,?AsyncParameter] , OptionalParameter [?Await,?AsyncParameter]
OptionalParameter [Await,AsyncParameter]:
[+AsyncParameter] AccessibilityModifier opt BindingIdentifier [Await] ? TypeAnnotation opt
[+AsyncParameter] AccessibilityModifier opt BindingIdentifier [Await] TypeAnnotation opt Initialiser [In]
[+AsyncParameter] BindingIdentifier [Await] ? : StringLiteral
[~AsyncParameter] AccessibilityModifier opt BindingIdentifier [?Await] ? TypeAnnotation opt
[~AsyncParameter] AccessibilityModifier opt BindingIdentifier [?Await] TypeAnnotation opt Initialiser [In,?Await]
[~AsyncParameter] BindingIdentifier [?Await] ? : StringLiteral
RestParameter [Await]:
... BindingIdentifier [?Await] TypeAnnotation opt
A.2 Expressions
BindingIdentifier [Await] : ( Modified )
Identifier but not await
[~Await] await
PropertyAssignment [Await] :
PropertyName : AssignmentExpression [?Await]
PropertyName CallSignature { FunctionBody }
GetAccessor
SetAccessor
async [no LineTerminator here] PropertyName CallSignature [Await,AsyncParameter] { FunctionBody [Await] }
GetAccessor :
get PropertyName ( ) TypeAnnotationopt { FunctionBody }
async [no LineTerminator here] get PropertyName ( ) TypeAnnotation opt { FunctionBody [Await] }
FunctionExpression [Await] : ( Modified )
function BindingIdentifier [?Await]opt CallSignature { FunctionBody }
async [no LineTerminator here] function BindingIdentifier [?Await]opt CallSignature [Await,AsyncParameter] { FunctionBody [Await] }
AssignmentExpression [Await] : ( Modified )
...
ArrowFunctionExpression [?Await]
ArrowFunctionExpression [Await] :
ArrowFormalParameters => Block [?Await]
ArrowFormalParameters => AssignmentExpression [?Await]
async [no LineTerminator here] CallSignature [Await,AsyncParameter] => Block [Await]
async [no LineTerminator here] CallSignature [Await,AsyncParameter] => AssignmentExpression [Await]
ArrowFormalParameters [Await] :
CallSignature
BindingIdentifier [?Await]
UnaryExpression [Await] : ( Modified )
...
< Type > UnaryExpression [?Await]
[+Await] await UnaryExpression [Await]
A.3 Functions
FunctionDeclaration [Await] : ( Modified )
FunctionOverloads [?Await]opt FunctionImplementation [?Await]
FunctionOverloads [Await] :
FunctionOverloads [?Await]opt FunctionOverload [?Await]
FunctionOverload [Await] :
function BindingIdentifier [?Await] CallSignature ;
FunctionImplementation [Await] :
function BindingIdentifier [?Await] CallSignature { FunctionBody }
async [no LineTerminator here] function BindingIdentifier [?Await] CallSignature [Await,AsyncParameter] { FunctionBody [Await] }
A.4 Classes
MemberFunctionImplementation:
AccessibilityModifieropt static opt PropertyName CallSignature { FunctionBody }
AccessibilityModifieropt static opt async [no LineTerminator here] PropertyName CallSignature [Await,AsyncParameter] { FunctionBody [Await] }
B Helper Functions
There are two helper functions that are used for Async Functions. The __awaiter helper function is used by both the ES6 as well as the ES5/ES3 transformations. The __generator helper function is used only by the ES5/ES3 transformation.
B.1 __awaiter helper function
var __awaiter = __awaiter || function (g) {
function n(r, t) {
while (true) {
if (r.done) return r.value;
if (r.value && typeof (t = r.value.then) === "function")
return t.call(r.value, function (v) { return n(g.next(v)) }, function (v) { return n(g["throw"](v)) });
r = g.next(r.value);
}
}
return n(g.next());
};
B.2 __generator helper function
var __generator = __generator || function (m) {
var d, i = [], f, g, s = { label: 0 }, y, b;
function n(c) {
if (f) throw new TypeError("Generator is already executing.");
switch (d && c[0]) {
case 0 /*next*/: return { value: void 0, done: true };
case 1 /*throw*/: throw c[1];
case 2 /*return*/: return { value: c[1], done: true };
}
while (true) {
f = false;
switch (!(g = s.trys && s.trys.length && s.trys[s.trys.length - 1]) && c[0]) {
case 1 /*throw*/: i.length = 0; d = true; throw c[1];
case 2 /*return*/: i.length = 0; d = true; return { value: c[1], done: true };
}
try {
if (y) {
f = true;
if (typeof (b = y[c[0]]) === "function") {
b = b.call(y, c[1]);
if (!b.done) return b;
c[0] = 0 /*next*/, c[1] = b.value;
}
y = undefined;
f = false;
}
switch (c[0]) {
case 0 /*next*/: s.sent = c[1]; break;
case 3 /*yield*/: s.label++; return { value: c[1], done: false };
case 4 /*yield**/: s.label++; y = c[1]; c[0] = 0 /*next*/; c[1] = void 0; continue;
case 6 /*endfinally*/: c = i.pop(); continue;
default:
if (c[0] === 1 /*throw*/ && s.label < g[1]) { s.error = c[1]; s.label = g[1]; break; }
if (c[0] === 5 /*break*/ && (!g || (c[1] >= g[0] && c[1] < g[3]))) { s.label = c[1]; break; }
s.trys.pop();
if (g[2]) { i.push(c); s.label = g[2]; break; }
continue;
}
f = true;
c = m(s);
} catch (e) {
y = void 0;
c[0] = 1 /*throw*/, c[1] = e;
}
}
}
return {
next: function (v) { return n([0 /*next*/, v]); },
1 /*throw*/: function (v) { return n([1 /*throw*/, v]); },
2 /*return*/: function (v) { return n([2 /*return*/, v]); },
};
};
B.2.1 _generator Arguments
| argument |
description |
m |
State machine function. |
B.2.2 n Arguments
| argument |
description |
c |
The current instruction. |
B.2.3 Variables
| variable |
description |
d |
A value indicating whether the generator is done executing. |
i |
A stack of instructions (see below) pending execution. |
f |
A value indicating whether the generator is executing, to prevent reentry (per ES6 spec). |
g |
The region at the top of the s.trys stack. |
s |
State information for the state machine function. |
s.label |
The label for the next instruction. |
s.trys |
An optional stack of protected regions for try..catch..finally blocks. |
s.sent |
A value sent to the generator when calling next. |
s.error |
An error caught by a catch clause. |
y |
The current inner generator to which to delegate generator instructions. |
b |
The method on n to execute for the current instruction. One of "next", "throw", or "return". |
B.2.4 Instructions
| instruction |
args |
description |
0 /*next*/ |
0-1 |
Begin or resume processing with an optional sent value. |
1 /*throw*/ |
1 |
Throw an exception at the current instruction. Executes any enclosing catch or finally blocks. |
2 /*return*/ |
0-1 |
Returns a value from the current instruction. Executes any enclosing finally blocks. |
3 /*yield*/ |
0-1 |
Suspends execution and yields an optional value to the caller of the generator. |
4 /*yield**/ |
1 |
Delegates generator operations to the provided iterable (not needed for Async Functions, but provided for future support for down-level Generator Functions). |
5 /*break*/ |
1 |
Jumps to a labeled instruction. Executes any enclosing finally blocks if the target is outside of the current protected region. |
6 /*endfinally*/ |
0 |
Marks the end of a finally block so that the previous break, throw, or return instruction can be processed. |
B.2.5 Protected Regions
A protected region marks the beginning and end of a try..catch or try..finally block. Protected regions are pushed onto the s.trys stack whenever a protected region is entered when executing the state machine. A protected region is defined using a quadruple in the following format:
| field |
required |
description |
| 0 |
yes |
The start of a try block. |
| 1 |
no |
The start of a catch block. |
| 2 |
no |
The start of a finally block. |
| 3 |
yes |
The end of a try..catch..finally block. |
B.3 __generator helper function (alternate)
var __generator = __generator || function (body) {
var done, instructions = [], stepping, region, state = { label: 0 }, delegated;
function step(instruction) {
if (stepping) throw new TypeError("Generator is already executing.");
if (done) {
switch (instruction[0]) {
case 0 /*next*/: return { value: void 0, done: true };
case 1 /*throw*/: throw instruction[1];
case 2 /*return*/: return { value: instruction[1], done: true };
}
}
while (true) {
stepping = false;
var region = state.trys && state.trys.length && state.trys[state.trys.length - 1];
if (region) {
switch (instruction[0]) {
case 1 /*throw*/:
instructions.length = 0;
done = true;
throw instruction[1];
case 2 /*return*/:
instructions.length = 0;
done = true;
return { value: instruction[1], done: true };
}
}
try {
if (delegated) {
stepping = true;
var callback = delegated[instruction[0]];
if (typeof callback === "function") {
var result = callback.call(delegated, instruction[1]);
if (!result.done) return result;
instruction[0] = 0 /*next*/;
instruction[1] = result.value;
}
delegated = undefined;
stepping = false;
}
switch (instruction[0]) {
case 3 /*yield*/:
state.label++;
return { value: instruction[1], done: false };
case 4 /*yield**/:
state.label++;
delegated = instruction[1];
instruction[0] = 0 /*next*/;
instruction[1] = void 0;
continue;
case 0 /*next*/:
state.sent = instruction[1];
break;
case 6 /*endfinally*/:
instruction = instructions.pop();
continue;
default:
if (instruction[0] === 5 /*break*/ && (!region || (instruction[1] >= region[0] && instruction[1] < region[3]))) {
state.label = instruction[1];
break;
}
if (instruction[0] === 1 /*throw*/ && state.label < region[1]) {
state.error = instruction[1];
state.label = region[1];
break;
}
state.trys.pop();
if (region[2]) {
instructions.push(instruction);
state.label = region[2];
break;
}
continue;
}
stepping = true;
instruction = body(state);
} catch (e) {
delegated = void 0;
instruction[0] = 1 /*throw*/, instruction[1] = e;
}
}
}
return {
next: function (v) { return step([0 /*next*/, v]); },
"throw": function (v) { return step([1 /*throw*/, v]); },
"return": function (v) { return step([2 /*return*/, v]); },
};
};Reacted by Stepan Mikhailiuk, rafis, Serhii Sol, Markus Springer and SlurpTheo Activitychanged the title [-]Spec Proposal: Async Functions[/-] [+]Proposal: Async Functions[/+] on Jan 14, 2015MgSam commented on Jan 14, 2015 This proposal looks great. Really looking forward to having this in TypeScript as asynchrony is so important, yet such a pain, in the JS world. I'm glad to see you're planning on supporting ES3/ES5 with async as well. rbuckton commented on Jan 14, 2015 ContributorAuthor More actions I've added a section on promise implementation compatibility. csnover commented on Jan 15, 2015 Still thinking about the proposal. One super nitpicky request off the top: is it possible to use human-readable variable names in the generator code instead of single letters? An optimiser needs to run over all code anyway for production, and will take care of shortening variables, so this is just obfuscation. When users examine code to understand and learn things, it will be much easier for them to understand what is going on with proper variable names. (This is an issue with the default generated __extends right now too, which is a much simpler function.) bgever commented on Jan 15, 2015 How are failed or rejected promises handled with this proposal?
I guess you'd need to handle them with try/catch? rbuckton commented on Jan 15, 2015 ContributorAuthor More actions Bart Verkoeijen (@bgever), that is correct. In an Async Function you could change from writing this:
function logCommits(): Promise<void> {
var client = new HttpClient();
return client.getAsync('https://github.com/Microsoft/TypeScript/commits/master.atom').then(
response => {
console.log(response.responseText);
},
error => {
console.log(error);
});
}
To this:
async function logCommits(): Promise<void> {
var client = new HttpClient();
try {
var response = await client.getAsync('https://github.com/Microsoft/TypeScript/commits/master.atom');
console.log(response.responseText);
}
catch (error) {
console.log(error);
}
}
This also allows for more complex async logic that is harder to achieve with Promise chaining, such as awaiting a promise in a loop:
async function poll(): Promise<void> {
do {
var dataReady = await isDataReadyAsync();
} while (!dataReady);
}bgever commented on Jan 15, 2015 Thanks for the quick response. Something interesting in your first example is that there's no need to return the result of the promise like before, since the async keyword will trigger this. A common mistake that can be prevented with async/await.
What I dislike about the catch clause, is that you can't use the functional programming way of just passing the function. E.g: .then(successHandler, errorHandler).
Would it be useful to have an inline option as well?
function errorHandler(e) { /* handle */ }
//---or---
var errorHandler = (e) => { /* handle */ };
// Handle errors inline without the need to wrap with try{}.
var result = await myAsync() catch errorHandler;
//---or---
var result = await myAsync() catch e => {
/* handle */
}
The errorHandler of the inline catch could even return a promise, which would allow the result of await to recover, if that new promise is resolved, or still throw an exception in case of rejection or when no promise is returned from the error handler.
var result = await myAsync() catch async e => { // `async` inline catch returns promise
if(/*can't recover*/){
throw new Error('Cannot recover');
}
return 'fallback value';
}
//---or---
var result = await myAsync() catch async e => /*can't recover*/ ? throw new Error('Cannot recover') : 'fallback value';johnnyreilly commented on Jan 15, 2015 rbuckton commented on Jan 15, 2015 ContributorAuthor More actions Bart Verkoeijen (@bgever), some Promise implementations, such as the native implementation in ES6, have a catch method you could use here:
var result = await myAsync().catch(errorHandler);
//---or---
var result = await myAsync().catch(e => {
// ...
});Steve-Fenton commented on Jan 15, 2015 bgever commented on Jan 15, 2015 Ron Buckton (@rbuckton), ah, of course! As long as we feed a Promise to await - including chained ones - it should work. Thanks for pointing that out. fletchsod-developer commented on Jan 15, 2015 wiktor-k commented on Sep 9, 2016 jods4 commented on Sep 9, 2016 Wiktor Kwapisiewicz (@wiktor-k) I don't quite agree kneden looks simpler. For the trivial return x.then(y), sure, but in general...
Note that in your gist, _generator and _await are helper functions, emitted once for the whole code base. The code that is actually generated for your async functions are x() and y(), which are not that bad once you understand how they work -- basically, one case for each branch of code in your original function. And because of this, the generated code maps pretty closely to the original code. This nice property is very not true for complex control structures in kneden.
Have you looked at how kneden translates a try/catch/finally with awaits? Brace yourself:
function test() {
return Promise.resolve().then(function () {
return Promise.resolve().then(function () {
return db.destroy();
}).catch(function (err) {
return Promise.resolve().then(function () {
console.log(err);
return db.post({});
}).then(function (_resp) {
console.log(_resp);
});
}).then(function () {
return db.info();
}, function (_err) {
return Promise.resolve().then(function () {
return db.info();
}).then(function () {
throw _err;
});
});
}).then(function () {});
}
Personally, I much prefer the y() code generated by TS. I wouldn't be surprised if it allocates less (hence performs better) as well.
As soon as there is complex control flow (loops, try, etc) using Promise only to write async code becomes very tricky. There is no "similar to how a human would do so" as most of my colleagues would be unable to write the code for even a simple loop.
Another important aspect -- but I might be wrong here -- is that it's hard to guarantee kneden supports all valid JS code and produces correct code (homepage says "it tries"). Notably, at this point it doesn't support return from inside switch and try/catch/finally blocks. MatAtBread commented on Sep 9, 2016 I don't know kneden, but nodent https://github.com/MatAtBread/nodent does implement all JS constructs containing async/await using Promises (or generators, or callbacks) through syntax transformation, and is reasonably mature. It also passes through all ES6 features. The main advantage is performance - it's significantly faster than Promise/generator implementations, including the built-in async/await support in Chrome 54. There's also covers for Babel, browserify and webpack.
Disclaimer: I'm the author of nodent. jods4 commented on Sep 9, 2016 Matt (@MatAtBread) I had a look at your project and I was very impressed! 👏
The approach when generating Promises is indeed different from kneden.
I have to admit that I was surprised the generated code performs so well.
I noticed the following in the playground, though. It seems to me that when using Promises the following ES7 code generates ES5 code that results in recursive calls of arbitrary depth (in this case, the ES5 looks like it will nest 3'000'000 function calls, i.e. 3 per loop iteration).
async function x() {
for (let i = 0; i < 1000000; i++) {
if (i == 999999) await a;
}
}
Turning loop iterations into recursive calls has serious drawbacks. If I am right, I think this makes it not ok for a widely used compiler like TS. MatAtBread commented on Sep 9, 2016 You're quite right about loops without tall-recursion. There's a discussion of the issue in MatAtBread/fast-async#11
The solution (the one I use in almost ask my production code) is to use Promises over pure-ES5 mode. MatAtBread commented on Sep 9, 2016 ... actually, I've only just understood your case - it only awaits rarely. That's a good one, I might try and handle that one differently to avoid the recursion, since that code path doesn't need it. Thanks for the input! matAtWork commented on Sep 26, 2016 jods4 commented on Sep 26, 2016 Matt W (@matAtWork) nice work. I was curious how you'd solve it!
I am still intrigued that this is 30% faster than native async/await despite all the calls and captures! matAtWork commented on Sep 26, 2016 Probably not the place to spend ages on it, but JS is the proof that "premature optimization is the root of all evil". In particular, a lot of performance hits on older JS engines have been heavily fixed - closures are a perfect example (now almost as fast as object dereferencing), and Promises are, well, promising.
When I started Nodent almost 3 years ago, avoiding Promises was a very easy win (Nodent only used callbacks). Since then, Promise implementations have been honed down, and the JS engines are much better at identifying and optimizing pinch points, so the benefits of avoiding them have fallen.
I'm guessing the same will be true of generators, although it's not there yet - right now they're still 3x-4x slower.
Meanwhile, the kind of transforms Nodent does really are pretty much what everyone in Node-land does by hand (identifying common code to put in functions and pass as callbacks), of which there is so much around I'm sure the engine writers have squeezed lots of performance out of it. JohnGalt1717 commented on Dec 8, 2016 Now that this is added how does one await an array of promises? I have a ton of ng.IPromise and I want to execute them all at the same time and then await them all completing. bman654 commented on Dec 8, 2016 `const arrayOfResults = await Promise.all(arrayOfPromises);`
…
--
Brandon Wallace
Senior Architect, Expero Inc.
@bman654 | experoinc.com | 832-312-7294
jamiewinder commented on Dec 8, 2016 Also handy:
const [a, b, c] = await Promise.all([Promise.resolve(1), Promise.resolve(true), Promise.resolve('a')]);
// a is number, b is boolean, c is stringJohnGalt1717 commented on Dec 8, 2016 Promise.all doesn't seem to work with ng.IPromise so I'm using $q.all right now which sadly isn't strongly typed on the results. I was hoping that there was a better way. RReverser commented on Dec 8, 2016
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|