Remove indentations in a tree of promises - javascript

I want to remove indentations in a javascript funtion that inside has a variable that recive the value of a promise. Inside of it has a try-catch with some code and inside the try section has a fetch with his own then and catch.
My question is, how can I simplify the code, even creating new functions if needed, to reduce the amount of indentations.
Thanks for advance.
Here an example code:
function getData(url) {
const myVariable = new Promise((resolve, reject) => {
try {
fetch(url)
.then((resp) => resp.json())
.then(json) => {
//some code here
});
// more code here
resolve();
})
.catch(() => { // this catch is of the fetch
// more code here
resolve();
});
} catch (error) {
// more code here
reject();
}
});
}
Y tried to transform the fetch using the ES6 async-await but I can not use an async funtion inside a promise

You don't need a new Promise. Once you have a promise -- like returned by fetch() -- you don't need another promise to tell you when the first one resolves. The same is true when you have a promise coming from then() calls: you don't need an extra promise for them. It is an antipattern to use the promise constructor callback just to create another promise (using some API).
Your example code has syntax issues (there are more closing braces than opening), so the .catch() method is not called on a promise. Your comment suggests you want to call it on the promise returned by fetch, but then it should be chained before the first then call. Each then() returns a new promise, and maybe you wanted to chain .catch() on the last .then(). In that case it will catch rejections of any of the previously chained promises, which means the outer try/catch block is not going to ever execute the catch block -- there is nothing remaining that can cause a run time error.
Another issue with the code (once its syntax errors are fixed) is that although myVariable will be assigned a promise:
That promise will resolve to undefined
The getData function will return a promise that will resolve to undefined
That is not very useful. You should resolve that promise with the data you got from the json() promise.
You can use async await like so:
async function getData(url) {
try {
const resp = await fetch(url);
} catch(err) {
console.log("fetch failed with following error", err);
throw err; // rethrow so caller's promise gets rejected
}
try {
const data = await resp.json();
} catch(err) {
console.log("json() failed with following error", err);
throw err; // rethrow so caller's promise gets rejected
}
return data;
}
As in this example the error handling is nothing more than just reporting on errors and then bubble the error upwards, it would make sense that the caller would take the responsibility to report on errors:
function getData(url) {
fetch(url).then(resp => resp.json());
}

Related

Why is my async '.then' not waiting until fetch is completed?

From what I understand about promises I should be able to run something in a '.then' after the promise returns and expect that a fetch is completed at that time. But for some reason I cannot get a page to reload AFTER the fetch is done to a node.js app.
fetch("http://localhost:3000/").then(response => {
console.log(response);
}).catch(error => {
console.log(error);
}).then(window.location.reload(false));
I even tried putting a timer on it to see if just needed more time on the server side, but it still runs immediately, ignoring the timer:
.then(function() {
window.location.reload(false)
}, 5000);
Could this be from how the fetch interacts with a node.js app or am I missing something in my implementation?
the problem is that when you set window.location.reload(false) after the then, it is being called at that same moment, in order to wait for the function to trigger, you need to pass an empty function and it will be triggered after.
fetch("https://api.github.com/orgs/nodejs").then(response => {
console.log("we get response")
console.log(response);
}).catch(error => {
console.log(error);
}).then(() => {
console.log("we move...")
// the line is commented so it doesn't loop infinitely.
//window.location.reload(false)
});
what you are doing is something like this:
you are executing the function inmediatly instead of waiting for the resolve.
const newPromise = new Promise((resolve, reject) => {
setTimeout(function() {
console.log('foo')
resolve(true);
}, 1500);
});
newPromise.then(console.log("anything"))
the correct way should be:
const newPromise = new Promise((resolve, reject) => {
setTimeout(function() {
console.log('foo')
resolve(true);
}, 1500);
});
newPromise.then(()=>{
console.log("anything")
})
then expects a function, not a statement. Try .then(() => window.location.reload(false)). A statement will just be executed when evaluated, so you end up with your page reloading (I'm guessing infinitely?)
As we know the concept of "Promise" from ES5 standards in which it makes code non-blocking and has callback function in itself like .then() or .catch() for error handling.
Here you trying to make code sync so here is the way you can use async-await as fetch() return promise resolve or reject to some value unlike
Eg;
const employeeList = async () => {
fetchResult = await fetch("https://api-dev.abc/employees");
return fetchResult;
}
console.log(employeeList());
Await used in async pause then the synchronous flow of execution and wait until promise come with resolved or reject value. Above fetchResult will have either resolved value or rejected value or say error. If we are not using async-await then we have to use .then() callback function for accessing value resolved or rejected by Promise.
Await is used to await callback hell problem in async code of ECMA.

Catching a promise without reject

I was going through express-async-handler code
const asyncUtil = fn =>
function asyncUtilWrap(...args) {
const fnReturn = fn(...args)
const next = args[args.length-1]
return Promise.resolve(fnReturn).catch(next)
}
module.exports = asyncUtil
Here they have used .catch without rejecting a promise and created a promise without using a promise constructor (new Promise(resolve, reject))
We use the above snippet of code like this
const asyncHandler = require('express-async-handler')
express.get('/', asyncHandler(async (req, res, next) => {
const bar = await foo.findAll();
res.send(bar)
}))
Can someone help me in comprehending what I am missing here?
fnReturn might be a promise, which would mean that the promise created with Promise.resolve would adopt it. The adopted promise might throw an error which needs catching
const fnReturn = fn(...args)
Here fn might be returning a promise which is getting passed as parameter to Promise.resolve and every time below code will return an resolved promise even if fnReturn throws an error and in this catch block may not be executed on rejection.
And more over Promise.resolve or Promise.reject is an another approach to return resolvedPromise value or rejected promise value.
please find below snippet to understand it better.
let a = Promise.resolve(13);
a.then((value)=>{console.log(value)});
More over to add on to this foo.findAll() returns a promise which we consume it using async await based on latest ES6 features and if you don't mind consume await under an try block so that if any error is encountered then it can be captured in catch block.
try{
const bar = await foo.findAll()
}
catch(exception){
console.log(exception);
}
created a promise without using a promise constructor
I saw something wrong in previous answer : fnReturn doesn’t have to be a Promise itself. It even has nothing to do with it. As specified in documentation :
The resolve function returns either a new promise resolved with the passed argument, or the argument itself if the argument is a promise produced by this constructor.
ecma.org
Which means, in other word, that argument passed to Promise.resolve() can be a synchrone function, which will be wrapped inside a new Promise. So that answer your first question : they don’t use Promise constructor because they don’t have to, javascript is doing it for them (like in a try{…} catch{…} function).
Here they have used .catch without rejecting a promise
When trying to figure out something, it is always interesting to play a bit around with your code. Let’s take a look at the following snippet:
// Original code
const asyncUtil = fn =>
function asyncUtilWrap(...args) {
const fnReturn = fn(...args);
const next = args[args.length-1];
return Promise.resolve(fnReturn).catch(next);
};
// Simulation
const req = {
url: "random"
};
const res = {
html: "rendered"
};
function next(err) {
alert(err.message);
return
}
asyncUtil(async (req, res, next) => {
// throw new Error("An error occured here");
const result = await new Promise(resolve => {
setTimeout(() => {resolve("Result of async function")}, 1000);
});
alert(result);
})(req, res, next);
This code intend to simulate an express environment… well, at least in the way we are interested in. I defined req and res as 2 fake objects, and next() to just display our error, if any. asyncUtil() will launch a function that resolve after 1 second, and display its result.
First run of the snippet should give the following result :
Notice the 3 commented lines. Here, everything went fine, and we got our result displayed. So what happens exactly here.
const fnReturn = fn(...args); This line assign our async function to fnReturn (fnReturn = async (req, res, next) => {. . .}). So when we call Promise.resolve(fnReturn), what it actually does is wrapping it inside a Promise like this :
new Promise(resolve => {
resolve(fnReturn);
});
Once completed, it will asynchronously return the result of fnReturn to callback.
Now our promise can only resolve, meaning it doesn’t have a reject on itself. But that doesn’t mean errors can’t occur. If you try to uncomment the following line :
throw new Error("An error occured here");
What we’ll do is throwing a fake error at the beginning of our function. Notice the result when running this :
Our regular script wasn’t executed (it breaks after an error), and we moved to our next() function !
Our Promise doesn’t have any error handler, but if an error is thrown, even outside a reject(), it’ll still propagate. Here, the Promise can’t resolve as it is broke in process, and our catch() will be able to block the exception, avoiding your whole code shutting down.
Hope it was clear enough, feel free to ask if anything wasn’t understandable !

Trying to use Async and Await from different functions

While I thought I was using Async and Await keywords properly, it appears as though I am doing something wrong.
I am using PouchDB, which is a great Javascript database that syncs with CouchDB. PouchDB uses promises for many of its functions, and has native support for Async and Await. As an example, to retrieve the basic information about a database, you would use the following code:
db.info().then(function (info) {
console.log(info);
})
I understand this, and it works. But if I try and put this inside a function, and then call that function, things go haywire. And I'm sure it's me and not PouchDB that is the problem...
function getLocalDBInfo(db){
try {
db.info().then(function (info) {
return info;
});
} catch (error) {
console.log("can't get local DB info: ", error);
}
}
async function testing(db){
try {
var info=await getLocalDBInfo(db);
await console.log("DB info=", info);
await console.log("Doc count= ", info.doc_count);
}
catch(err){
console.log("error=",err);
}
//info contains...
//{"doc_count":0,"update_seq":0,"db_name":"kittens"}
}
testing(MY_db);
If I log info inside the getLocalDBInfo function, it eventually (ie. after the promise fulfills) logs info to the console. But, the logs inside the testing function return immediately with undefined. It makes sense to me that they are returning undefined because they are returning immediately, but I was trying to have them wait for info by using async and await. Any suggestions as to what I am doing wrong?
getLocalDBInfo() is not returning a promise, so you can't await for it.
with async/await you could:
async function getLocalDBInfo(db){
try{
return await db.info()
} catch (err){
console.log("can't get local DB info: ", error);
}
}
you can also use new promise
getLocalDBInfo generates a promise, attaches a callback to it, then returns undefined. It doesn't return the resolved promise and you can't await on an undefined (well, you can, but it won't do anything).
You neee to change getLocalDBInfo to actually return the promise.
Also, why do you have a then block with the signature info => info? All this will do is unwrap the async value and then wrap it again. You can omit the then callback entirely.
Similarly, are you sure you need to wrap the db.info call in a try/catch? I wonder if you might be intending to use a promise.catch here instead. The try/catch construct only grabs promise rejections when awaiting an expression.
You need to return the value from the outer function getLocalDBInfo
function getLocalDBInfo(db) {
return new Promise((resolve, reject) => {
db.info()
.then(info => resolve(info))
.catch(err => reject(err))
})
}

Throwing inside a catch block in a nested Promise to trigger the catch block of outer Promise, is there an alternative cleaner way?

I'm nesting Promises and I have to know whether the nested Promise is a rejected Promise or a fulfilled Promise to know whether to trigger the outer Promise chain's catch. To differentiate if the nested Promise is rejected or fulfilled, I use a throw in my nested Promise's catch to indicate rejection; while fulfillment is always indicated when there's no throw in my nested Promise's catch. Please take a look at the example below:
let test = new Promise((resolve, reject) => {
resolve(42);
}).then((e) => {
console.log(e);
return new Promise((resolve, reject) => { //Error happens inside this nested Promise
resolve(32);
}).then((e) => {
console.log(e);
//Run other processes that may lead to an error
}).catch((e) => { //I added this catch block for error detection, whatever error it might be
console.log(e);
throw(e); //I'm throwing (e) from the inner Promise's catch so that the returned Promise in the outer then is a rejected Promise, which will be "caught" by the catch block of the outer Promise
});
}).catch((e) => {
console.log(e); //I handle error that happen may either in the inner Promise or the outer Promise here
});
The above shows what I meant by throw-ing inside the catch block of my nested Promise. Is the above the standard way to indicate that a nested Promise failed, or is there an alternative cleaner method to achieve what I want? As you can see, I'm practically throw-ing twice inside my nested Promise to indicate rejection. Is there a way where I can throw once and indicate Promise rejection?
EDIT
The reasons I'm using a catch block in my inner Promise and my outer Promise: I want to detect error inside my inner Promise, and said detection is done using my inner catch block; I want to handle the error that may happen in the inner Promise or in the outer Promise with the same handler, and that is done using my outer catch block. Because catch-ing inside my inner Promise returns a Promise that is considered fulfilled to my outer Promise's then block, I decided to use throw inside my inner catch block to indicate that it actually isn't fulfilled if it reached the inner catch block. I've also edited my code to indicate that the error happening inside my inner Promise is not manually triggered by me throw-ing in the code.
I think the clean way to do this would be using async/await. But before going there, is your question how to not run the outer promise when the inner promise fails?
The example below:
When the inner promise rejects, the chain stops.
When the outer promise rejects, the inner is already fulfilled.
const fun42 = () => {
return new Promise((resolve, reject) => {
setTimeout(() =>{
resolve(42)
reject('something at fun 42 went wrong')
}, 500)
})
}
const fun32 = () => {
return new Promise((resolve, reject) => {
setTimeout(() =>{
//resolve(32)
reject('something at fun 32 went wrong')
}, 500)
})
}
fun32().then(x => {
console.log(x)
return fun42()
}).then( y => {
console.log(y)
}).catch (e => {
console.log(e)
})
Well, it's a question of design. Error handling should happen in one place on each level (like you did in your example). The nested catch function handles the error the decide whether it should propagate it or quietly finish it (like your wrote).
In that specific example I would use the reject function of the wrapping Promise to reject it, other than throwing an error.

Not being able to use promise

I'm trying to use promises in node.
But, the then part is not being executed.
The first function returns a resolve() instance.
How can I fix this?
This is the code:
exports.refresh_access_token = function (environment_hash) {
...
Model.update(values, {where: where}).then(function () {
console.log('updated!');
resolve("Success!");
}).catch(function (err) {
console.log('error on update');
});
...
}
async.map(accounts, function (account) {
module.exports.refresh_access_token(account.environment_hash).then(function () {
console.log('async called back');
});
}
It's not 100% clear what you're asking, but there are several errors you can fix:
To let the caller know when an internal promise is done, you must return that promise. So, add return to returnModel.update(...)`
resolve() is not a globally available function so there's no point in trying to call it from within a .then() handler. In fact, that probably causes an exception to be thrown because resolve is not defined.
When you are inside a .then() handler, the original promise is already resolved. You don't need to resolve anything. To return a value as the resolved value of the parent promise, just return that value.
When you log from within a .catch() handler, if you want the host promise to stay rejected, you have to re-throw the error. Otherwise, the error becomes "handled" and the promise changes to resolved.
Then, in your second code block, it really does not make sense to mix the async library with promises. They are different approaches to managing asynchronous operations. Pick one scheme or the other - don't mix. If you already have a promise like you do, then you can just return that promise and let the caller use the promise. No need for the async library when you already have promises.
You can fix those like this:
exports.refresh_access_token = function (environment_hash) {
...
return Model.update(values, {where: where}).then(function () {
console.log('updated!');
return "Success!";
}).catch(function (err) {
console.log('error on update');
// after logging, make sure promise stays rejected
throw err;
});
...
}
You need to return your promise to be able to use then
return Model.update(...)

Categories