ES8 Immediately invoked async function expression - javascript

I haven't seen these constructs used much but I've found myself writing them to make use of async / await in functions that wouldn't typically return a promise, for example
chan.consume(queue, (msg) => {
this.pendingMsgs++; // executed immediately
(async () => {
await this.handleMessage(msg);
this.pendingMsgs--;
if (cancelled && this.pendingMsgs === 0) {
await chan.close();
await this.amqpConnectionPool.release(conn);
}
})();
});
as opposed to
chan.consume(queue, async (msg) => { // external lib does not expect a return value from this callback
this.pendingMsgs++; // executed in promise context(?)
await this.handleMessage(msg);
this.pendingMsgs--;
if (cancelled && this.pendingMsgs === 0) {
await chan.close();
await this.amqpConnectionPool.release(conn);
}
});
or
chan.consume(queue, (msg) => {
this.pendingMsgs++; // no await - excess function decls & nesting
this.handleMessage(msg).then(() => {
this.pendingMsgs--;
if (cancelled && this.pendingMsgs === 0) {
chan.close().then(() => {
this.amqpConnectionPool.release(conn);
});
}
});
});
Is this 'a thing'? Are there pitfalls here I should be aware of?
What's the lowdown on use of async / await in these kind of situations?

Is this 'a thing'?
Yes. It comes up every now and then, e.g. here. They're known as IIAFEs :-)
If you want to put focus on the arrow, you could also call them IIAAFs.
Are there pitfalls here I should be aware of?
Whenever you call a promise-returning function and don't return the result to somewhere else, you are responsible for the promise yourself - which means that you have to handle errors from it. So the pattern should in general look like
(async () => {
…
})().catch(err => {
console.error(err);
});
if you don't want to concern yourself with unhandled-rejection events.
What's the lowdown on use of async/await in these kind of situations?
Not much, compared to the then version. However, you say "the external lib does not expect a return value from this callback", which might hint at the library's incompatibility with asynchronous callbacks, so beware what you are doing when. It also might depend on exceptions being thrown synchronously from the callback, so it all depends on what the library expects here (and if there are no expectations, whether that may change in the future). You don't want future incompatibilities in case the library will start to treat promise return values specially.
However, I would still recommend the second pattern that directly passes the async function directly as the callback because of its better readability. If you want to avoid returning a promise to the library, create a helper function that wraps the callback:
function toVoid(fn) {
return (...args) => void fn(...args);
}
function promiseToVoid(fn) {
return (...args) => void fn(...args).catch(console.error);
}
which you could use like this:
chan.consume(queue, toVoid(async (msg) => {
… // use `await` freely
}));

(async () => {
await func();
})();

Related

Best way to handle case where same code must run either immediately or after a promise resolved

Depending on a certain condition x, I need to perform a redirect either right now or after a promise asyncFunction resolved:
if (x) {
await asyncFunction();
redirectToA();
}
redirectToA();
That's quite ugly though. Is there a way to simplify this code so that redirectToA(); appears only once?
I think you could simply do this:
if (x) {
await asyncFunction();
}
redirectToA();
This is an example showing you that the redirectToA() will wait:
async function asyncFunction() {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, 3000)
})
}
function redirectToA() {
document.getElementById('status').innerText = "Redirected"
}
(async function() {
// your code is here
if (true) {
await asyncFunction()
}
redirectToA()
})()
<p id="status">
Running
</p>
It may not be an issue but I would be inclined to call redirectToA() at nextTick+ under both conditions.
await (x ? asyncFunction() : Promise.resolve());
redirectToA();
Thus, you are better guaranteed consistent redirect behaviour and may possibly benefit from less testing.

Can async functions accept and execute callback functions?

I've been really intrigued about async / await syntax recently and I've been experimenting with it for a while.
In this particular problem, my goal is to execute the callback function after all the promises are made, if possible.
I had no idea how to apply my ideas in real life situations so I expected the function to look like this.
function endTask (task) {
// finalizes tasks
}
var taskCompleted = false;
async function doSomething (callback) {
const response = await fetch(some_url);
const task = await response.json();
if (task) {
taskCompleted = true;
}
if (typeof callback == "function" && taskCompleted) {
callback(task);
}
}
doSomething(endTask);
The purpose of the async/wait functionality is to reduce the complexity called Callback Hell. Async functions effectively removes the necessity of passing around callback functions.
However, one can still pass a callback function to an async function as a reference to be called later in his logic. This is perfectly alright as long as the developers do not go to the extent of wrapping Promises around async functions. That application goes against the concept of async/await.
Async functions always take parameters in real practice and some of them are callback functions which are to be called immediately or pass on to another function.
Shorthand version of your code:
function endTask (task) {
// finalizes tasks
}
async function doSomething () {
const response = await fetch(some_url);
const task = await response.json();
if (typeof callback == "function" && task) {
endTask (task);
}
}
doSomething();
I think if you already used async/await, maybe you don't need to use callback anymore. The purpose of async/await functions is to simplify the behavior of using promises, and avoid callback hell too.
Hope the example below could help.
function endTask (task) {
// finalizes tasks
}
async function doSomething (callback) {
const task = await fetch(some_url).then(res => res.json());
return task;
}
async function main() {
const task = await doSomething();
if (task) {
endTask()
}
}
Yes, async functions can take and call callbacks just like any other function, but that pretty much defeats the purpose of using an async function. There are some cases where there is a valid reason to do this (see Dave Newton's comment below), but this isn't one of them.
Instead, you should do something like this:
function endTask(task) {
// finalizes tasks
}
var taskCompleted = false;
async function doSomething() {
const response = await fetch(some_url);
const task = await response.json();
return task;
}
doSomething()
.then(task => {
if (task) {
taskCompleted = true;
}
endTask(task);
})
.catch(error => {
// handle error
});

The correct way to invoke an async function main?

I have some nodejs scripts - i.e. processes which do a job and complete, rather than run continuously.
I use async functions, for example:
const mysql = require('mysql2/promise');
...
async function main() {
var conn = await mysql.createConnection(config.mysql);
...
var [response, error] = await conn.execute(`
DELETE something
FROM some_table
WHERE field = ?
`, [value]);
...
Is the following code:
main().then(() => process.exit(0)).catch(err => { console.error(err); process.exit(1); });
the best/correct way to start execution of the async code?
(It works, but I want to make sure that I'm not leaving any holes which might cause surprises, such as exceptions being silently swallowed.)
Why does conn.execute() return an error (which I need to manually check) rather than throwing one?
The use of then together with async..await isn't necessary because it's syntactic sugar for then.
Entry point could be async IIFE (IIAFE):
(async () => {
try {
var conn = await mysql.createConnection(config.mysql);
...
var [response] = await conn.execute(`
SELECT something
FROM some_table
WHERE field = ?
`, [value]);
...
process.exit(0);
} catch (err) {
console.error(err);
process.exit(1);
}
})();
There also may be no need for process.exit(0) if the connection was closed.
Why does conn.execute() return an error (which I need to manually check) rather than throwing one?
It doesn't, and isn't conventional for promise-enabled functions to return an error in a result.
Callback-based execute uses error-first callback for errors. Promise-based execute can't throw an error because it returns a promise which is rejected in case of error.
As the documentation shows, the second element is fields and not error:
const [rows, fields] = await conn.execute('select ?+? as sum', [2, 2]);
It may return rejected promise which can be caught with try..catch inside async function in case of error.
The async function declaration defines an asynchronous function, which returns an AsyncFunction object. An asynchronous function is a function which operates asynchronously via the event loop, using an implicit Promise to return its result. But the syntax and structure of your code using async functions is much more like using standard synchronous functions.
You can also define async functions using an async function expression.
async function f() {
try {
let response = await fetch('http://no-such-url');
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}
f();
you can also immediately call an asynce function using the syntax below
(async () => {
})();

Using async and await in selenium protractor tests

How do I use async and await in protractor tests?
it('test async', function(){
var value = 0;
function asyncAction() {
return browser.driver.wait(()=>true)
.then(function () {
console.log('a');
return value++;
});
}
//-Problem Area-
async function doAwait(){
await asyncAction();
return asyncAction();
}
doAwait();
protractor.promise.controlFlow().execute( () => {
console.log('b');
expect(value).toBe(2);
});
});
output here is
a
b
a
and value is 1 at time of expect
function doAwait(){
await asyncAction();
return asyncAction();
}
I like to think of this as similar to
function doAwait(){
asyncAction().then(()=>asyncAction());
}
Which works but the above async doAwait does not. I believe this is because the generator breaks the ControlFlow of selenium.
Adding this to the protractor configuration works:
var webdriver = require.main.require('selenium-webdriver');
Promise = webdriver.promise.Promise;
Object.assign(Promise, webdriver.promise);
Promise.resolve = Promise.fulfilled;
Promise.reject = Promise.rejected;
Though maybe not all promises are supposed to be managed promises?
Worth noting that the other solution requires wrapping each async function:
protractor.promise.controlFlow().execute( async () => {
await asyncAction();
return asyncAction();
});
See https://github.com/angular/jasminewd#async-functions--await:
async functions / await
async functions and the await keyword are likely coming in ES2017 (ES8), and available via several compilers. At the moment, they often break the WebDriver control flow. (GitHub issue). You can still use them, but if you do then you will have to use await/Promises for almost all your synchronization. See spec/asyncAwaitAdapterSpec.ts and spec/asyncAwaitErrorSpec.ts for examples.

How to know if a function is async?

I have to pass a function to another function, and execute it as a callback. The problem is that sometimes this function is async, like:
async function() {
// Some async actions
}
So I want to execute await callback() or callback() depending on the type of function that it is receiving.
Is there a way to know the type of the function??
Theory
Native async functions may be identifiable when being converted to strings:
asyncFn[Symbol.toStringTag] === 'AsyncFunction'
Or by AsyncFunction constructor:
const AsyncFunction = (async () => {}).constructor;
asyncFn instanceof AsyncFunction === true
This won't work with Babel/TypeScript output, because asyncFn is regular function in transpiled code, it is an instance of Function or GeneratorFunction, not AsyncFunction. To make sure that it won't give false positives for generator and regular functions in transpiled code:
const AsyncFunction = (async () => {}).constructor;
const GeneratorFunction = (function* () => {}).constructor;
(asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true
Since native async functions were officially introduced to Node.js in 2017, the question likely refers to Babel implementation of async function, which relies on transform-async-to-generator to transpile async to generator functions, may also use transform-regenerator to transpile generator to regular functions.
The result of async function call is a promise. According to the proposal, a promise or a non-promise may be passed to await, so await callback() is universal.
There are only few edge cases when this may be needed. For instance, native async functions use native promises internally and don't pick up global Promise if its implementation was changed:
let NativePromise = Promise;
Promise = CustomPromiseImplementation;
Promise.resolve() instanceof Promise === true
(async () => {})() instanceof Promise === false;
(async () => {})() instanceof NativePromise === true;
This may affect function behaviour (this is a known problem for Angular and Zone.js promise implementation). Even then it's preferable to detect that function return value is not expected Promise instance instead of detecting that a function is async, because the same problem is applicable to any function that uses alternative promise implementation, not just async (the solution to said Angular problem is to wrap async return value with Promise.resolve).
Practice
From the outside, async function is just a function that unconditionally returns native promise, therefore it should be treated like one. Even if a function once was defined async, it can be transpiled at some point and become regular function.
A function that can return a promise
In ES6, a function that potentially returns a promise can be used with Promise.resolve (lets synchronous errors) or wrapped Promise constructor (handles synchronous errors):
Promise.resolve(fnThatPossiblyReturnsAPromise())
.then(result => ...);
new Promise(resolve => resolve(fnThatPossiblyReturnsAPromiseOrThrows()))
.then(result => ...);
In ES2017, this is done with await (this is how the example from the question is supposed to be written):
let result = await fnThatPossiblyReturnsAPromiseOrThrows();
...
A function that should return a promise
Checking if an object is a promise is a matter of a separate question, but generally it shouldn't be too strict or loose in order to cover corner cases. instanceof Promise may not work if global Promise was replaced, Promise !== (async () => {})().constructor. This can happen when Angular and non-Angular applications interface.
A function that requires to be async, i.e. to always return a promise should be called first, then returned value is checked to be a promise:
let promise = fnThatShouldReturnAPromise();
if (promise && typeof promise.then === 'function' && promise[Symbol.toStringTag] === 'Promise') {
// is compliant native promise implementation
} else {
throw new Error('async function expected');
}
TL;DR: async functions shouldn't be distinguished from regular functions that return promises. There is no reliable way and no practical reason to detect non-native transpiled async functions.
As long as only the native async functions are used (which is usually the case), I prefer this simple way:
theFunc.constructor.name == 'AsyncFunction'
Both #rnd, and #estus are correct.
But to answer the question with an actual working solution here you go
function isAsync (func) {
const string = func.toString().trim();
return !!(
// native
string.match(/^async /) ||
// babel (this may change, but hey...)
string.match(/return _ref[^\.]*\.apply/)
// insert your other dirty transpiler check
// there are other more complex situations that maybe require you to check the return line for a *promise*
);
}
This is a very valid question, and I'm upset that someone down voted him. The main usecase for this type of checking is for a library/framework/decorators.
These are early days, and we shouldn't downvote VALID questions.
In case you're using NodeJS 10.x or later
Use the native util function.
util.types.isAsyncFunction(function foo() {}); // Returns false
util.types.isAsyncFunction(async function foo() {}); // Returns true
Do keep all the concerns in mind from above ansers though. A function that just returns by accident a promise, will return a false negative.
And on top of that (from the docs):
Note that this only reports back what the JavaScript engine is seeing; in particular, the return value may not match the original source code if a transpilation tool was used.
But if you use async in NodeJS 10 and no transiplation. This is a nice solution.
It seems that await can be used for normal functions too. I'm not sure if it can be considered "good practice" but here it is:
async function asyncFn() {
// await for some async stuff
return 'hello from asyncFn'
}
function syncFn() {
return 'hello from syncFn'
}
async function run() {
console.log(await asyncFn()) // 'hello from asyncFn'
console.log(await syncFn()) // 'hello from syncFn'
}
run()
Here is a short and useful approach provided by David Walsh in his blogpost:
const isAsync = myFunction.constructor.name === "AsyncFunction";
Cheers!
TL;DR
Short answer: Use instaceof after exposing AsyncFunction - see below.
Long answer: Don't do that - see below.
How to do it
You can detect whether a function was declared with the async keyword
When you create a function, it shows that it's a type Function:
> f1 = function () {};
[Function: f1]
You can test it with the instanceof operator:
> f1 instanceof Function
true
When you create an async function, it shows that it's a type AsyncFunction:
> f2 = async function () {}
[AsyncFunction: f2]
so one might expect that it can be tested with instanceof as well:
> f2 instanceof AsyncFunction
ReferenceError: AsyncFunction is not defined
Why is that? Because the AsyncFunction is not a global object. See the docs:
https://developer.mozilla.org/Web/JavaScript/Reference/Global_Objects/AsyncFunction
even though, as you can see, it's listed under Reference/Global_Objects...
If you need easy access to the AsyncFunction then you can use my unexposed module:
https://www.npmjs.com/package/unexposed
to get either a local variable:
const { AsyncFunction } = require('unexposed');
or to add a global AsyncFunction alongside other global objects:
require('unexposed').addGlobals();
and now the above works as expected:
> f2 = async function () {}
[AsyncFunction: f2]
> f2 instanceof AsyncFunction
true
Why you shouldn't do it
The above code will test whether the function was created with the async keyword but keep in mind that what is really important is not how a function was created but whether or not a function returns a promise.
Everywhere where you can use this "async" function:
const f1 = async () => {
// ...
};
you could also use this:
const f2 = () => new Promise((resolve, reject) => {
});
even though it was not created with the async keyword and thus will not be matched with instanceof or with any other method posted in other answers.
Specifically, consider this:
const f1 = async (x) => {
// ...
};
const f2 = () => f1(123);
The f2 is just f1 with hardcoded argument and it doesn't make much sense to add async here, even though the result will be as much "async" as f1 in every respect.
Summary
So it is possible to check if a function was created with the async keyword, but use it with caution because you when you check it then most likely you're doing something wrong.
You can assume at begin that callback is promise:
export async function runSyncOrAsync(callback: Function) {
let promisOrValue = callback()
if (promisOrValue instanceof Promise) {
promisOrValue = Promise.resolve(promisOrValue)
}
return promisOrValue;
}
and them in your code you can do this:
await runSyncOrAsync(callback)
which will solve your problem with unknowing callback type....
Full Solution: Handle both Async and Promise
I always use Promises and async/await interchangeably, as they are basically the same.
Async/Await is used to work with promises in asynchronous functions. It is basically syntactic sugar for promises. It is just a wrapper to restyle code and make promises easier to read and use.
Source: GeeksForGeeks
If you need a helper function to determine if a value is an asynchronous function, without invoking it, or if a value is a function that returns a Promise, you have arrived at the right post.
In this example I will present three different approaches.
Check if function is an async/await function.
Check if a regular function returns a Promise.
Check both.
Handle Async Functions
This function can determine if a function was defined using the async keyword.
Example functions to validate
async function a() {}
const b = async () => {}
Validation function
function isAsyncFunction(f: unknown): boolean {
return f && f.constructor.name === 'AsyncFunction'
}
Handle Promise Functions
This function can determine if a regular function returns a Promise. In order assess if the given function returns a Promise, we need to invoke the function and examine the returned value. To avoid multiple invocations of the same function, we can return the the aforementioned value if it's a Promise, and false if it's not.
Example functions to validate
function a() { return new Promise(() => {}) }
const b = () => new Promise(() => {})
Validation function
function isPromiseFunction<T>(fn: any, ...params: any[]): Promise<T> | boolean {
const isFunction = fn && typeof fn === 'function'
const notAsyncFunction = fn.constructor.name !== 'AsyncFunction'
if (isFunction && notAsyncFunction) {
const value = fn(...params) || false
if (value && value.constructor.name === 'Promise') {
return value as Promise<T>
}
}
return false
}
Handle both
Because both AsyncFunction and Promise are essentially the same, we can just check if they both return a Promise.
function isAsync<T>(fn: any, ...params: any[]): Promise<T> | boolean {
const isFunction = fn && typeof fn === 'function'
if (isFunction) {
const value = fn(...params) || false
if (value && value.constructor.name === 'Promise') {
return value as Promise<T>
}
}
return false
}
Conclusion
Asynchronous functions are faster and cleaner to validate, whereas promise functions need to be invoked in order to be validated.
Test Functions (CodePen)
https://codepen.io/isakhauge/pen/gOGGQPY
So I want to execute await callback() or callback() depending on the
type of function that it is receiving.
You could always execute it with await and it will do the right thing:
async function main(callback) {
let result = await callback(); // even if callback is not async
// use 'result'
}
Is there a way to know the type of the function??
Maybe what you're actually interested in is the type of the result of the function. Dariusz Filipiak's answer is good but it can be even more concise:
async function main(callback) {
let result = callback();
if (result instanceof Promise) {
result = await result;
}
// use 'result'
}

Categories

Resources