async function nested inside a function - javascript

I'm using a framework where I found code like this:
block1
fun_1(params, callback) {
fun_2(params, callback) {
...
fun_n(params, callback) {
asyncFunction().then(callback).catch(callback)
}
as asyncFunction is from a deprecated npm package i would take the opportunity to refactor it.
I would like to switch to something like this:
block2
fun_1(params).then(callback)
Where fun_1 would be:
fun_1(params) {
fun_2(params) {
return asyncFunc() ???
}
}
Is the second pattern correct and preferable over the first ?

It seems correct. However, all functions need to return that promise when they call the inner function. For example:
fun_1(params) {
fun_2(params) {
return asyncFunc();
}
return fun_2(params);
}
fun_1(params).then(callback);

There's not much information in your question, but I'll try to answer with what I have.
In order for fun_1 to be able to be chained with .then, it needs to return a promise:
function fun_1() {
return new Promise((resolve, reject) => {
// Do some tasks and resolve when complete
resolve(/* some data */)
}
}
Or, by using the async keyword, which is just syntactic sugar for Promise:
async function fun_1() {
await some_async_task()
return; // does the same as resolve would in Promise
}
In order to refactor the code, you're going to have to move the async stuff and the callbacks in promises.
Let me know if you want to clarify some things in your question to get a better answer.

Related

Is it possible to run an asynchronous function without making the main function as async?

Is it possible to run an asynchronous function without making the main function as async?
function index() {
console.log('1');
aaa();
console.log('3');
}
async function aaa() {
return await bbb().then((value) => {
console.log(value);
});
}
async function bbb() {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve('2');
}, 1000);
});
}
index();
It is possible to display:
1
2
3
Without making the index function as async?
You need to do something to cause console.log('3') to run after the promise returned by aaa() has been resolved.
Making index async and using await is the approach that most people find easiest to write and maintain.
async function index() {
console.log('1');
await aaa();
console.log('3');
}
You could use then() instead.
function index() {
console.log('1');
aaa().then(
() => { console.log('3'); }
);
}
Since aaa returns a Promise, you can always interact directly with that Promise instead of using await. For example:
function index() {
console.log('1');
aaa().then(() => console.log('3'));
}
That way the code in the .then() callback won't execute until after the Promise returned by aaa() resolves.
For the most part, without peeking under the hood to the inner workings of JavaScript, async and await tends to just be a cleaner and simpler way of expressing this same thing. For this simple example, there really shouldn't be a meaningful difference between this:
function index() {
console.log('1');
return aaa().then(() => console.log('3'));
}
And this:
async function index() {
console.log('1');
await aaa();
console.log('3');
}
The main difference between these and the function at the top of this answer is that the one at the top doesn't advertise that it internally has asynchronous operations. So it can't be awaited. If that's not an issue for your needs, no big deal. But even then, going with async and await still does the same thing and consuming code can simply choose not to await this function.

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
});

How do use Javascript Async-Await as an alternative to polling for a statechange?

I'd like to accomplish the following using promises: only execute further once the state of something is ready. I.e. like polling for an external state-change.
I've tried using promises and async-await but am not getting the desired outcome. What am I doing wrong here, and how do I fix it?
The MDN docs have something similar but their settimeout is called within the promise--that's not exactly what I'm looking for though.
I expect the console.log to show "This function is now good to go!" after 5 seconds, but instead execution seems to stop after calling await promiseForState();
var state = false;
function stateReady (){
state = true;
}
function promiseForState(){
var msg = "good to go!";
var promise = new Promise(function (resolve,reject){
if (state){
resolve(msg);
}
});
return promise;
}
async function waiting (intro){
var result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
What you're doing wrong is the promise constructor executor function executes immediately when the promise is created, and then never again. At that point, state is false, so nothing happens.
Promises (and async/await) are not a replacement for polling. You still need to poll somewhere.
The good news: async functions make it easy to do conditional code with loops and promises.
But don't put code inside promise constructor executor functions, because of their poor error handling characteristics. They are meant to wrap legacy code.
Instead, try this:
var state = false;
function stateReady() {
state = true;
}
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
async function promiseForState() {
while (!state) {
await wait(1000);
}
return "good to go!";
}
async function waiting(intro) {
var result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
Based on your comments that you are waiting for messages from a server it appears you are trying to solve an X/Y problem. I am therefore going to answer the question of "how do I wait for server messages" instead of waiting for global variable to change.
If your network API accepts a callback
Plenty of networking API such as XMLHttpRequest and node's Http.request() are callback based. If the API you are using is callback or event based then you can do something like this:
function myFunctionToFetchFromServer () {
// example is jQuery's ajax but it can easily be replaced with other API
return new Promise(function (resolve, reject) {
$.ajax('http://some.server/somewhere', {
success: resolve,
error: reject
});
});
}
async function waiting (intro){
var result = await myFunctionToFetchFromServer();
console.log(intro + result);
}
If your network API is promise based
If on the other hand you are using a more modern promise based networking API such as fetch() you can simply await the promise:
function myFunctionToFetchFromServer () {
return fetch('http://some.server/somewhere');
}
async function waiting (intro){
var result = await myFunctionToFetchFromServer();
console.log(intro + result);
}
Decoupling network access from your event handler
Note that the following are only my opinion but it is also the normal standard practice in the javascript community:
In either case above, once you have a promise it is possible to decouple your network API form your waiting() event handler. You just need to save the promise somewhere else. Evert's answer shows one way you can do this.
However, in my not-so-humble opinion, you should not do this. In projects of significant size this leads to difficulty in tracing the source of where the state change comes form. This is what we did in the 90s and early 2000s with javascript. We had a lot of events in our code like onChange and onReady or onData instead of callbacks passed as function parameters. The result was that sometimes it takes you a long time to figure out what code is triggering what event.
Callback parameters and promises forces the event generator to be in the same place in the code as the event consumer:
let this_variable_consumes_result_of_a_promise = await generate_a_promise();
this_function_generate_async_event((consume_async_result) => { /* ... */ });
From the wording of your question you seem to be wanting to do this instead;
..somewhere in your code:
this_function_generate_async_event(() => { set_global_state() });
..somewhere else in your code:
let this_variable_consumes_result_of_a_promise = await global_state();
I would consider this an anti-pattern.
Calling asynchronous functions in class constructors
This is not only an anti-pattern but an impossibility (as you've no doubt discovered when you find that you cannot return the asynchronous result).
There are however design patterns that can work around this. The following is an example of exposing a database connection that is created asynchronously:
class MyClass {
constructor () {
// constructor logic
}
db () {
if (this.connection) {
return Promise.resolve(this.connection);
}
else {
return new Promise (function (resolve, reject) {
createDbConnection(function (error, conn) {
if (error) {
reject(error);
}
else {
this.connection = conn; // cache the connection
resolve(this.connection);
}
});
});
}
}
}
Usage:
const myObj = new MyClass();
async function waiting (intro){
const db = await myObj.db();
db.doSomething(); // you can now use the database connection.
}
You can read more about asynchronous constructors from my answer to this other question: Async/Await Class Constructor
The way I would solve this, is as follows. I am not 100% certain this solves your problem, but the assumption here is that you have control over stateReady().
let state = false;
let stateResolver;
const statePromise = new Promise( (res, rej) => {
stateResolver = res;
});
function stateReady(){
state = true;
stateResolver();
}
async function promiseForState(){
await stateResolver();
const msg = "good to go!";
return msg;
}
async function waiting (intro){
const result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
Some key points:
The way this is written currently is that the 'state' can only transition to true once. If you want to allow this to be fired many times, some of those const will need to be let and the promise needs to be re-created.
I created the promise once, globally and always return the same one because it's really just one event that every caller subscribes to.
I needed a stateResolver variable to lift the res argument out of the promise constructor into the global scope.
Here is an alternative using .requestAnimationFrame().
It provides a clean interface that is simple to understand.
var serverStuffComplete = false
// mock the server delay of 5 seconds
setTimeout(()=>serverStuffComplete = true, 5000);
// continue until serverStuffComplete is true
function waitForServer(now) {
if (serverStuffComplete) {
doSomethingElse();
} else {
// place this request on the next tick
requestAnimationFrame(waitForServer);
}
}
console.log("Waiting for server...");
// starts the process off
requestAnimationFrame(waitForServer);
//resolve the promise or whatever
function doSomethingElse() {
console.log('Done baby!');
}

What is the (potential) drawback of using async functions everywhere?

I am learning Javascript. This may sound like a crazy idea but I don't find clear answers on Google. Can I just replace all my regular functions/methods with async function everywhere? I mean there seems to be no downsides to this and it makes things so much simpler. Where the situation only involves synchronous steps, you just don't use await and it will work just like normal functions. Easy!
Personally I find having to distinguish async functions and normal functions unnecessarily burdensome. It's like driving a manual transmission car, where the car itself could easily handle the gearing by itself.
Am I missing something here?
async functions always return Promises. This means that anytime you're not dealing with something asynchronous, you would have to convert the returned Promise into a value before you could use it. For example:
const return4 = async () => 4;
console.log(4 + return4());
Instead, you would have to use the (unnecessarily wordy):
const return4 = async () => 4;
(async () => {
console.log(4 + await return4());
})();
(or call .then on the return4 call before using it)
If return4 was not async, on the other hand, of course console.log(4 + return4()); alone would work just fine.
Another problem with async functions is that transpiling them to ES5 code (which allows for compatibility with obsolete browsers like IE) requires regenerator-runtime, which is very heavyweight. For example, transpiling the following single line with Babel:
const foo = async () => console.log('async!');
When you plug it into the repl, you get:
"use strict";
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
var foo =
/*#__PURE__*/
(function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
return _context.abrupt("return", console.log("async!"));
case 1:
case "end":
return _context.stop();
}
}
}, _callee);
})
);
return function foo() {
return _ref.apply(this, arguments);
};
})();
which also depends on regeneratorRuntime already being included in the script, which is 700-something lines of code that you can see here.
On less powerful systems, this can result in a not-insignificant performance hit. This is why some (such as with the AirBNB style guide) prefer to never use async functions, even if they make the asynchronous control flow of the script clearer.

Passing an extra parameter in the middle of a Promise chain

I need to throw in a userID param in the middle of a promise chain(it is the only promise that needs it). All the promises should execute in a synchronous order.
SideNote- All the similar examples on stackoverflow are all a bit different- like using lambda functions(I use declared functions).So I'm still not quite sure.
var initialize = function(userID) {
var firstPromise = Module2.getFirstPromise();
firstPromise.then(getSecondPromise)
.then(getThirdPromise)
.then(getFourthPromise) //<----Fourth promise needs userID
.then(getFifthPromise)
.then(Utils.initializeComplete);
}
All the promises are functions that look like this:
var thirdPromise = function() {
return new Promise(function(resolve, reject) {
//fetch data
//On Return, Store it
resolve() //Nothing needed to passed down from this promise
});
});
}
I'm trying this, and it "works", but I'm not sure if that is how I am "suppose" to handle something like this. :)
var initialize = function(userID) {
var firstPromise = Module2.getFirstPromise();
firstPromise.then(getSecondPromise)
.then(getThirdPromise)
.then(function(){ return fourthPromise(userID)})
.then(getFourthPromise)
.then(Utils.initializeComplete);
}
Note: getFirstPromise is coming from a different module in my code. That shouldn't be important to the question though :)
Assuming that firstPromise is really a promise but secondPromise and so on are actually functions returning promises, then yes, what you've done is how you're supposed to do that. Here's a live example on Babel's REPL, which looks like this:
function doSomething(userID) {
getFirstPromise()
.then(getSecondPromise)
.then(getThirdPromise)
.then(() => getFourthPromise(userID))
// Or .then(function() { return getFourthPromise(userID); })
.then(getFifthPromise)
.catch(err => {
console.log("Error: " + err.message);
});
}
doSomething("foo");
function getFirstPromise() {
console.log("getFirstPromise called");
return new Promise(resolve => {
setTimeout(() => {
resolve("one");
}, 0);
});
}
// and then second, third, fourth (with user ID), and fifth
(If you don't use arrow functions, just replace them with the function form.)
Note the catch in the example above. Unless you have a really good reason not to, a promise chain should always have a .catch if you don't return the result.
Your solution is perfectly fine. It might be easier to understand with concrete signatures.
If your thirdPromise doesn't take anything and doesn't return anything its signature might be written (pseudocode assuming a -> b is a function from a to b) as _ -> Promise (_). If it returns some value a, it would be _ -> Promise (a). If it took something and returned something it might be a -> Promise (b)
So you can reason about your promise chains as about functions taking some value and returning some other value wrapped in a promise. However, your fourthPromise looks differently:
fourthPromise : UserId -> a -> Promise (b)
Which can be written as:
fourthPromise : UserId -> (a -> Promise (b))
It takes one parameter before becoming an actual promise you can chain. In a way, it's a template of a promise.
If you want the plain .then chain in the main function, try to write getFourthPromise as a factory function
function getSomethingByUserID(userId) {
return function() {
return new Promise(function(resolve) {
//your actual async job
resolve('a result');
});
};
}
Then you will get plan list of thens
var firstPromise = Module2.getFirstPromise()
.then(getSecondPromise)
.then(getSomethingByUserID(userId))
.then(Utils.initializeComplete);
Do not forget that if you missed to provide userId to getSomethingByUserID, it will not work.

Categories

Resources