JavaScript - Attach context to async function call? - javascript

The bounty expires in 19 hours. Answers to this question are eligible for a +100 reputation bounty.
David Callanan wants to draw more attention to this question.
Synchronous function call context
In JavaScript, it's easy to associate some context with a synchronous function call by using a stack in a global scope.
// Context management
let contextStack = [];
let context;
const withContext = (ctx, func) {
contextStack.push(ctx);
context = ctx;
try {
return func();
} finally {
context = contextStack.pop();
}
};
// Example
const foo = (message) => {
console.log(message);
console.log(context);
};
const bar = () => {
withContext("calling from bar", () => foo("hello"));
};
This allows us to write context-specific code without having to pass around a context object everywhere and have every function we use depend on this context object.
This is possible in JavaScript because of the guarantee of sequential code execution, that is, these synchronous functions are run to completion before any other code can modify the global state.
Generator function call context
We can achieve something similar with generator functions. Generator functions give us an opportunity to take control just before conceptual execution of the generator function resumes. This means that even if execution is suspended for a few seconds (that is, the function is not run to completion before any other code runs), we can still ensure that there is an accurate context attached to its execution.
const iterWithContext = function* (ctx, generator) {
// not a perfect implementation
let iter = generator();
let reply;
while (true) {
const { done, value } = withContext(ctx, () => iter.next(reply));
if (done) {
return;
}
reply = yield value;
}
};
Question: Async function call context?
It would also be very useful to attach some context to the execution of an async function.
const timeout = (ms) => new Promise(res => setTimeout(res, ms));
const foo = async () => {
await timeout(1000);
console.log(context);
};
const bar = async () => {
await asyncWithContext("calling from bar", foo);
};
The problem is, to the best of my knowledge, there is no way of intercepting the moment before an async function resumes execution, or the moment after the async function suspends execution, in order to provide this context.
Is there any way of achieving this?
My best option right now is to not use async functions, but to use generator functions that behave like async functions. But this is not very practical as it requires the entire codebase to be written like this.
Background / Motivation
Using context like this is incredibly valuable because the context is available deep down the call-stack. This is especially useful if a library needs to call an external handler such that if the handler calls back to the library, the library will have the appropriate context. For example, I'd imagine React hooks and Solid.js extensively use context in this way under-the-hood. If not done this way, the programmer would have to pass a context object around everywhere and use it when calling back to the library, which is both messy and error-prone. Context is a way to neatly "curry" or abstract away a context object from function calls, based on where we are in the call stack. Whether it is good practice or not is debatable, but I think we can agree that it's something library authors have chosen to do. I would like to extend the use of context to asynchronous functions, which are supposed to conceptually behave like synchronous functions when it comes to the execution flow.

As far as I know ECMA has no specification for "contexts" (regardless if it's a normal or async function).
Therefore the solution you posted for normal functions is already a hack.
As per ECMA standard, there is no JavaScript based API to hook await in order to do a generator like trick. So you have to rely on (environment based) hacks.
These hacks may highly depend on the environment you're using.
JavaScript Only (requires async stack traces)
A solution which is purly based async stack traces is the following one.
Since nearly every JavaScript interpreter is based on V8 this works on nearly every use case.
const kContextIdFunctionPrefix = "__context_id__";
const kContextIdRegex = new RegExp(`${kContextIdFunctionPrefix}([0-9]+)`);
let contextIdOffset = 0;
function runWithContextId(target, ...args) {
const contextId = ++contextIdOffset;
let proxy;
eval(`proxy = async function ${kContextIdFunctionPrefix}${contextId}(target, ...args){ return await target.call(this, ...args); }`);
return proxy.call(this, target, ...args);
}
function getContextId() {
const stack = new Error().stack.split("\n");
for(const frame of stack) {
const match = frame.match(kContextIdRegex);
if(!match) {
continue;
}
const id = parseInt(match[1]);
if(isNaN(id)) {
console.warn(`Context id regex matched, but failed to parse context id from ${match[1]}`);
continue;
}
return id;
}
console.log(new Error().stack)
throw new Error("getContextId() called without providing a context (runWithContextId(...))");
}
A simple demo:
async function main() {
const target = async () => {
const contextId = getContextId();
console.log(`Context Id: ${contextId}`);
await new Promise(resolve => setTimeout(resolve, 1000));
console.log(`Context Id (After await): ${getContextId()} (before: ${contextId})`);
return contextId;
};
const contextIdA = runWithContextId(target);
const contextIdB = runWithContextId(target);
// Note: We're first awaiting the second call!
console.log(`Invoke #2 context id: ${await contextIdB}`);
console.log(`Invoke #1 context id: ${await contextIdA}`);
}
main();
This solution leverages stack traces in order to identify a context id. Traversing the (sync and async) stack trace and using dynamically generated functions with special names allows to pass a special value (a number in this instance).
NodeJS (AsyncLocalStorage)
NodeJS offers a way for Asynchronous context tracking:
https://nodejs.org/api/async_context.html#class-asynclocalstorage
It should be possible to build an async context by using AsyncLocalStorage.
Using a transpiler
You might want to use a transpiler (like babel or typescript) which convert async functions to generator functions on the fly.
Using a transpiler allows you to even write a plugin for implementing async contexts based on generator functions.

Related

Is it possible to tell if an async function is currently running at the top of the "program"?

I'm trying to do something that involves a global context that knows about what function is running at the moment.
This is easy with single-threaded synchronous functions, they start off running and finish running when they return.
But async functions can pop to the bottom of the program and climb back up multiple times before completing.
let currentlyRunningStack: string[] = [];
function run(id: string, cb: () => any) {
currentlyRunningStack.push(id);
cb()
currentlyRunningStack.shift();
}
// works with synchronous stuff
run("foo", () => {
run("bar", () => console.log("bar should be running"));
console.log("now foo is running");
});
// can it work with asynchronous
run("qux", async () => {
// async functions never run immediately...
await somePromise();
// and they start and stop a lot
});
Is it possible to keep track of whether or not an asynchronous function is currently running or currently waiting on something?
EDIT: there appears to be something similar called Zone.js. Used by Angular I guess.
Something like async_hooks for the browser?
EDIT: per #Bergi's suggestion, the word "stack" has been updated to "program" for clarification
It is possible, and someone has done it -- the angular developers, no less -- but it costs a whopping 5.41Mb!!
https://www.npmjs.com/package/zone.js
This was ripped from this very similar question: Something like async_hooks for the browser?
In order to distinguish from that question a bit, I'll answer the core query here:
Yes, you can tell when an async function stops and starts. You can see a lot of what's required in the project code
In particular, this file appears to handle poly-filling promises, though I'd need more time to verify this is where the magic happens. Perhaps with some effort I can distill this into something simpler to understand, that doesn't require 5.41 Mb to acheive.
Yes, it possible.
Using a running context, like a mutex, provided by Edgar W. Djiskistra, stack queues, Promise states and Promise.all executor. This way you can keep track if there's a running function in the program. You will have to implement a garbage collector, keeping the mutex list clean and will need a timer(setTimeout) to verify if the context is clean. When the context is clean, you will call a callback-like function to end you program, like process.exit(0). By context we refeer to the entire program order of execution.
Transforming the function into a promise with an .then callback to pop/clean the stack of the mutex after the execution of content of the function with a try/catch block to throw, handle, or log errors add more control to the hole program.
The introduction of setTimeout propicies a state machine, combined with the mutex/lock and introduces a memory leak that you will need to keep track of the timer to clear the memory allocated by each function.
This is done by neste try/catch. The use of setInterval for it introduces a memory leak that will cause a buffer overflow.
The timer will do the end of the program and there's it. You can keep track if a function is running or not and have every function registered running in a syncrhonous manner using await with and mutex.
Running the program/interpreter in a syncrhonous way avoid the memory leaks and race conditions, and work well. Some code example below.
const async run (fn) => {
const functionContextExecutionStackLength = functionExecutionStackLength + 1
const checkMutexStackQueue = () => {
if (mutexStack[0] instanceof Promise) {
if (mutex[0].state == "fullfiled") {
mutexStack = mutexStack.split(1, mutexStack.length)
runner.clear()
runner()
}
}
if (mutexStack.length == 0) process.exit(0)
}
// clear function Exection Context
const stackCleaner = setTimeout(1000, (err, callback) => {
if (functionContextExecutionStackLength == 10) {
runner.clear()
}
})
stackCleaner = stackCleaner()
// avoid memory leak on function execution context
if (functionContextExecutionStackLength == 10) {
stackCleaner.clear()
stackCleaner()
}
// the runner
const runner = setTimeout(1, async (err, callback) => {
// run syncronous
const append = (fn) => mutex.append(util.promisfy(fn)
.then(appendFunctionExectionContextTimes)
.then(checkMutexStackQueue))
// tranaform into promise with callback
const fn1 = () => util.promify(fn)
const fn2 = () => util.promisfy(append)
const orderOfExecution = [fn1, fn2, fn]
// a for await version can be considered
for (let i = 0; i < orderOfExecution.length; i++) {
if (orderOfExecution.length == index) {
orderOfExecution[orderOfExecution.length]()
} else {
try {
orderOfExecution[i]()
} catch (err) {
throw err
console.error(err)
}
}
}
}
}
(() => run())(fn)
On the code above we take the assynchronous caracterisc of javascript very seriously. Avoiding it when necessary and using it when is needed.
Obs:
Some variables was ommited but this is a demo code.
Sometimes you will see a variables context switching and call before execution, this is due to the es modules characteriscts of reading it all and interpreting it later.

Why is async required to call await inside a JavaScript function body? [duplicate]

This question already has answers here:
Why do I have to put async keyword to functions which have await keywords?
(1 answer)
JS async/await - why does await need async?
(3 answers)
Why 'await' requires 'async' in function definition
(4 answers)
Nodejs why is await only restricted to async functions?
(3 answers)
ES2017 - Async vs. Yield
(4 answers)
Closed 2 years ago.
One has to use async keyword on the containing function to use the await inside the function body.
async function fetchMovies() {
const response = await fetch('/movies');
console.log(response);
}
fetchMovies();
The await is being used to block on completion of the asynchronous fetch() call. As can be seen in the code, the function fetchMovies() is not even returning any value. And even if it did, it would affect the way the return value is consumed by the caller, but why should it matter to the call to another asynchronous call from the function body?
My question is why is this required? Is there some good explaination of that? Is it related to the need for actual implementation of await and supporting it in older JavaScript versions?
I know iffi pattern is used be able to use await but does that change the semantics for the code that follows the iffi code block in any way?
(async () => {
const response = await fetch('/movies');
console.log(response);
})();
I am also aware of top level await being supported in modules.
May be I am missing something really obvious.
There are three reasons the async keyword exists:
In ECMAScript language versions prior to 2015, await was not a keyword. Marking a function async provides a syntactic "bailout" to indicate a breaking change in the language grammar within the body of the function.
This is the most important reason. Without the async keyword, all programs written in ECMAScript 5 or older would no longer work if they used the await keyword as a variable (in fact this was done intentionally in some cases as a polyfill before async/await was standardized), since that would cause a breaking change without the addition of async to the specification. Because of this, async is syntactically necessary to avoid breaking changes to the language.
It provides a convenient marker for parsers, avoiding an infinite look-ahead in order to determine whether or not a function is asynchronous.
This makes parsing more efficient, which is appealing for both ECMAScript implementers and developers, though this reason alone does not make async strictly necessary to the syntax.
async also performs its own transformation on the function, which is done regardless of whether or not the await keyword is present in the body.
Consider the following two functions:
function foo() {
if (Math.random() < 0.5) {
return 'return';
} else {
throw 'throw';
}
}
async function bar() {
if (Math.random() < 0.5) {
return 'return';
} else {
throw 'throw';
}
}
async performs the following transformation of function bar():
function bar() {
return new Promise((resolve, reject) => {
try {
resolve((/*async function bar*/() => {
if (Math.random() < 0.5) {
return 'return';
} else {
throw 'throw';
}
})());
} catch (reason) {
reject(reason);
}
});
}
Those familiar with promises will recognize that we can simplify the above since the Promise constructor executor function will implicitly reject if it throws an error synchronously:
function bar() {
return new Promise((resolve) => {
if (Math.random() < 0.5) {
return resolve('return');
} else {
throw 'throw';
}
});
}
There are two questions being asked here. Why do you need the async keyword to indicate an async context, and why do you need async context to use await?
If we didn't use the async keyword to indicate an async context, then we would have backwards compatibility issues. Before the async/await update, "await" was a valid variable name. So to make it a reserved word, we need to introduce a new context, the async function context.
The second question is, why do we want this in the first place? Why do we want to differentiate async functions from traditional synchronous code? As the name implies, async functions do not execute all their code at once. When a function "awaits", it effectively stops execution, and the remainder of the code becomes an event handler for the thing being awaited. This allows other code to run.
Browser javascript implementations are single-threaded. Only one thing can be performed at a time, but the event queue allows functions to take turns. Consider what would happen if you could await from a synchronous function. The synchronous code, by definition, does not give up control. Therefore, the async function it's waiting for will never get a chance to swap in to your single execution thread, and you will be waiting forever. So, you can only await an async function if you are already in an async context.
I assume your exact question is this: "Handling return values (null or something) depends on the consumer who called the function. They "should" supposedly get it even if another asynchronous function is called in-between. So why does it matter to wait before further other asynchronous calls?"
You see, such fetch() calls are done in Databases within the duration of "initiating" and "closing" the connection. Almost every method used is asynchronous in this case. So while you're executing fetchMovies(); The thread might move further and execute connection.close(); before the fetching is resolved and returned.
The exact scenarios are like this:
await client.connect(); //connection establishment
// Your purposeful methods
async function fetchMovies() {
const response = await fetch('/movies');
console.log(response);
}
await fetchMovies();
// Closing connection to avoid attacks and maintain concurrency
await client.close();
If any method, in this case, is called in an asynchronous manner, the whole session of a Database connection is wasted and our function would return undefined or throw an error "Cannot use a session that has ended"
So we need to "wait" for the "Pending" Promises to reach a "Fulfilled" or "Rejected" state before executing further calls.
You can refer more to this article: Using Promises, async / await with MongoDB just for the sake of understanding.
I think it's to make it clear that the function contains asynchronous code. Let's use another example that does return something:
async function canUseGeolocation() {
const result = await navigator.permissions.query({name: 'geolocation'});
return result.state;
}
The async keyword tells the javascript engine that the function should return a promise, and any return statements in the function should resolve that promise. What happens if we modify it to cache the values so we don't always call the await?
function canUseGeolocation() {
if (cachedPermissionState) return cachedPermissionState;
const result = await navigator.permissions.query({name: 'geolocation'});
cachedPermissionState = result.state;
return result.state;
}
How should javascript know that the function should return a promise? Because it contains an await? What if you later change the function so that the cachedPermissionState is set elsewhere and this function only returns that value so you remove the await? Should that first return statement return a promise or return the value? That would now change the whole way the function is executed and what is returned by return cachedPermissionState;. By using async, we can know that it really returns a promise that the return statement resolves without scanning the function for await statements to determine if it should be treated as async or not.

How to work around promises when using continuation-local-storage?

continuation-local-storage seems to be used, also in context of express.
Yet the very basic usage does not work for me, since the context is completely lost!
var createNamespace = require('continuation-local-storage').createNamespace;
var session = createNamespace('my session');
async function doAsync() {
console.log('Before getting into the promise: ' + session.get('test'));
await Promise.resolve();
console.log('*in* the promise: ' + session.get('test'));
}
session.run(() => {
session.set('test', 'peekaboo');
doAsync();
});
results in:
$ node server_test.js
Before getting into the promise: peekaboo
*in* the promise: undefined
Have I done something completely wrong or is the CLS simply broken?
Or is the library broken?
If it's not meant to work with promises, are there other concepts that work as a threadLocal storage to implement multi-tenancy in a proper way?
cls-hooked seems to be working fine, though the library (as the previous one) were last updated two years ago...
If someone has some other more robust way to implement a thread-local state for multi-tenancy please share!
You are trying to maintain a shared state across multiple functions which are then executes asynchronously. That is a very common case in JS, and the language itself provides a very simple, yet powerful mechanism for that: You can access variables from an outer function from an inner function, even if the outer function already finished its execution:
(function closured() {
let shared = { /*...*/ };
function inner() {
console.log( shared );
}
setTimeout(inner);
})();
Now while that works, it doesn't scale that well for larger applications: All functions accessing that state have to be inside of one function, and therefore that file gets really blown up. The libraries you are using are trying to resolve that, they also use closures to maintain the state across asynchronous calls: When you register an async callback the state is stored in a function, then the callback itself gets wrapped into a callback that restores the state:
let state;
function setTimeoutWithState(cb, time) {
const closured = state;
setTimeout(() => {
state = closured;
cb();
}, time);
}
state = 1;
setTimeoutWithState(() => console.log(state), 1000);
state = 2;
setTimeoutWithState(() => console.log(state), 500);
Now the library just has to wrap every async callback that way, and then you can maintain the state easily, isn't that great? Well for sure it is, but adding code to every callback does have it's cost (as JS heavily utilizes callbacks).

How to get fingerprint2 result like function

I want to make a function that get the reslt of fingerprint2.js
Fingerprint2 is a Modern & flexible browser fingerprinting library http://valve.github.io/fingerprintjs2/
Usage:
new Fingerprint2().get(function(result, components){
console.log(result); //a hash, representing your device fingerprint
console.log(components); // an array of FP components
});
whatever try i did to get result of Fingerprint2 outside of new Fingerprint2().get(function(result, components){ was failed.
like Global vars and cookie because Fingerprint2().get(...) is asynchronous
Can it be written like a function to get fingerprint2 result?
for example:
var secure = getmefingerprint2();
Leverage with ES2017 feature async/await, you can use Fingerprint2.getPromise() like this:
(async () => {
const components = await Fingerprint2.getPromise();
const values = components.map(component => component.value);
const murmur = Fingerprint2.x64hash128(values.join(""), 31);
console.log('fingerprint:', murmur);
)()
See get and getPromise in Fingerprint2 Doc
This should be a comment but its a bit long.
Even if it were possible, you would be bypassing the published api, meaning you would have to maintain a fork of the original code. You would also need to invoke the functionality synchronously - and fingerprintjs2 runs asynchronously for good and obvious reasons.
You seem to be asking about an XY problem
How you should sole it depends on what you intend to do with the fingerprint after it has been captured.
You can't make async code act completely synchronous. However, you can use async/await, if your target browser has support, but it's not universally supported. Also, it only looks synchronous inside the async function
The basic idea is to return a promise, then await it inside an async function:
const getmefingerprint2 = async () => {
const secure = await (new Promise(resolve => {
new Fingerprint2().get((result, components) => resolve(result) )
}))
// do things with secure, whatever you return is thenable
return secure
}
that function could be called like this (because of Promises):
getmefingerprint2().then(result => {
// do stuff with result
})
but also, inside the async function, you could treat secure like you got it synchronously.
If you really wanted to make your async code act more sync (might be useful for other async code, too, if you hate async), you could wrap all your code in an async function, then use await to get async stuff:
const getFingerprint = () => new Promise(resolve => {
new Fingerprint2().get((result, components) => resolve(result) )
})
const main = async () => {
// do some of your app here
const secure = await getFingerprint()
// do more stuff here
}
main()
Or as an IIFE:
(async() => {
// do some of your app here
const secure = await getFingerprint()
// do more stuff here
})()
These are just kinda hacky workarounds that allow you to escape the burden of async code, which is maybe worth just getting to know, as it will make a better app. If you restructure your code to only have the things that depend on secure inside the callback, you'll get better performance, unblocked UI, and a more dynamic flow that is easy enough to reason about, once you get used to it.

ES2017 - Async vs. Yield

I am confused about the current discussion of adding async functions and the keyword await to the next EcmaScript.
I do not understand why it is necessary to have the async keyword before the function keyword.
From my point of view the await keyword to wait for a result of a generator or promise done, a function's return should be enough.
await should simple be usable within normal functions and generator functions with no additional async marker.
And if I need to create a function what should be usable as an result for an await, I simply use a promise.
My reason for asking is this good explanation, where the following example comes from:
async function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// some more logic
}
It also could be done as normal function, if the execution of a function will wait for finishing the hole function until all awaits are fulfilled.
function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// return because createUser() and getFacebookFriends() and maybe inviteFacebookFriends() finished their awaited result.
}
In my opinion the whole function execution is holding until the next tick (await fulfillment) is done. The difference to Generator-Function is that the next() is triggering and changing the object's value and done field. A function instead will simple give back the result when it is done and the trigger is a function internal trigger like a while-loop.
I do not understand why it is necessary to have the async keyword before the function keyword.
For the same reason that we have the * symbol before generator functions: They mark the function as extraordinary. They are quite similar in that regard - they add a visual marker that the body of this function does not run to completion by itself, but can be interleaved arbitrarily with other code.
The * denotes a generator function, which will always return a generator that can be advanced (and stopped) from outside by consuming it similar to an iterator.
The async denotes an asynchronous function, which will always return a promise that depends on other promises and whose execution is concurrent to other asynchronous operations (and might be cancelled from outside).
It's true that the keyword is not strictly necessary and the kind of the function could be determined by whether the respective keywords (yield(*)/await) appear in its body, but that would lead to less maintainable code:
less comprehensible, because you need to scan the whole body to determine the kind
more errorprone, because it's easy to break a function by adding/removing those keywords without getting a syntax error
a normal function, whose execution will wait for finishing the hole body until all awaits are fulfilled
That sounds like you want a blocking function, which is a very bad idea in a concurrent setting.
By marking a function as async, you're telling JS to always return a Promise.
Because it will always return a Promise, it can also await on promises inside of its own block. Imagine it like one giant Promise chain - what happens internally to the function gets effectively gets bolted on to its internal .then() block, and what's returned is the final .then() in the chain.
For example, this function...
async function test() {
return 'hello world';
}
... returns a Promise. So you can execute it like one, .then() and all.
test().then(message => {
// message = 'hello world'
});
So...
async function test() {
const user = await getUser();
const report = await user.getReport();
report.read = true
return report;
}
Is roughly analogous to...
function test() {
return getUser().then(function (user) {
return user.getReport().then(function (report) {
report.read = true;
return report;
});
});
}
In both cases, the callback passed to test().then() will receive report as its first parameter.
Generators (i.e. marking a function * and using the yield keyword) are a different concept altogether. They don't use Promises. They effectively allow you to 'jump' between different portions of your code, yielding a result from inside of a function and then jumping back to that point and resuming for the next yield block.
Although they feel somewhat similar (i.e. 'halting' execution until something happens somewhere else), async/await only gives you that illusion because it messes with the internal ordering of Promise execution. It's not actually waiting - it's just shuffling when the callbacks happen.
Generators, by contrast, are implemented differently so that the generator can maintain state and be iterated over. Again, nothing to do with Promises.
The line is further blurred because at the current time of writing, support for async/await is scare; Chakracore supports it natively, and V8 has it coming soon. In the meantime, transpilers like Babel allow you to write async/await and convert the code to generators. It's a mistake to conclude that generators and async/await are therefore the same; they're not... it just so happens that you can bastardize how yield works alongside Promises to get a similar result.
Update: November 2017
Node LTS now has native async/await support, so you ought never to need to use generators to simulate Promises.
These answers all give valid arguments for why the async keyword is a good thing, but none of them actually mentions the real reason why it had to be added to the spec.
The reason is that this was a valid JS pre-ES7
function await(x) {
return 'awaiting ' + x
}
function foo() {
return(await(42))
}
According to your logic, would foo() return Promise{42} or "awaiting 42"? (returning a Promise would break backward compatibility)
So the answer is: await is a regular identifier and it's only treated as a keyword inside async functions, so they have to be marked in some way.
The reason for the async keyword in front is simple so you know that return value will be transformed into a promise. If no keyword how would the Interpreter know to do this.
I think this was first introduce in C# and EcmaScript is taking a loot of stuff from TypeScript. TypeScript and C# are conceived by Anders Hejlsberg and are similar.
lets say you have a function (this one is just to have some asynchronous work)
function timeoutPromise() {
return (new Promise(function(resolve, reject) {
var random = Math.random()*1000;
setTimeout(
function() {
resolve(random);
}, random);
}));
}
this function will make us wait for a random time and return a Promise (if you use jQuery Promise is similar to Deferred) object. To use this function today you would write something like this
function test(){
timeoutPromise().then(function(waited){
console.log('I waited' + waited);
});
}
And this is fine. Now lets try to return the log message
function test(){
return timeoutPromise().then(function(waited){
var message = 'I waited' + waited;
console.log(message);
return message; //this is where jQuery Deferred is different then a Promise and better in my opinion
});
}
Ok this is not bad but there are two return statements and a function in the code.
Now with async this will look like this
async function test(){
var message = 'I waited' + (await timeoutPromise());
console.log(message);
return message;
}
The code is short and inline. If you written a lot of .then() or . done() you know how unreadable the code can get.
Now why the async keyword in front of function. Well this is to indicate that your return value is not what is returned. In theory you could write this(This can be done in c# I don't know if js will allow since it's not done).
async function test(wait){
if(wait == true){
return await timeoutPromise();
}
return 5;
}
you see, you return a number but the actual return will be a Promise you don't have to use
return new Promise(function(resolve, reject) { resolve(5);};
Since you can't await a number only a Promise await test(false) will throw an exception and await test(true) will not if you don't indicate async in front.

Categories

Resources