Extract .then and .catch from promises - javascript

I have written a general .js crud object which holds all the methods used to communicate with the server. However I'd like to avoid the repetition of .then and .catch and I would like to abstract that functionality in an external method.
Not sure if what I'm trying to achieve is even possible.
My code below:
all(url, success, fail){
return new Promise((resolve,reject) => {
_get(url)
.then((response) => {
if (response.status == 200) {
success.call(this,response);
return resolve();
}
})
.catch((error) => {
fail.call(this, error);
reject(error);
});
});}, submit, update .....
Wonderland desired result:
all(url, success, fail){
return new Promise((resolve, reject) => {
_get(url).handle(args);
});
}

Just avoid the Promise constructor antipattern and callbacks, and you'll be good!
function all(url) {
return _get(url).then((response) => {
if (response.status == 200) {
return response;
}
// most likely you want to `throw` an error here?
});
}

Related

How to avoid propagation when chaining Promise with Non-Promise calls

I want the following code not to call OK logic, nor reject the promise. Note, I've a mixture of promise and non-promise calls (which somehow still managed to stay thenable after returning a string from its non-promise step), I just want the promise to stay at pending status if p1 resolves to non-OK value.
const p1 = new Promise((resolve, reject) => {
resolve("NOT_OK_BUT_NOT_A_CATCH_NEITHER");
});
p1.then(result => {
if (result !== "OK") {
return "How do I avoid calling OK logic w/out rejecting?";
}
else {
return Promise.resolve("OK");
}
}).then(result => {
console.error("OK logic...");
});
You've got two options:
1) throw an error:
p1.then(result => {
if (result =='notOk') {
throw new Error('not ok');
} else {
return 'OK';
}
})
.then(r => {
// r will be 'OK'
})
.catch(e => {
// e will be an error with message 'not ok', if it threw
})
the second .then won't run, the .catch will.
2) decide what to do in the latter .then conditionally:
p1.then(result => {
if (result =='notOk') {
return 'not ok'
} else {
return 'OK';
}
})
.then(r => {
if (r === 'OK') {
// do stuff here for condition of OK
}
})
This works because the second .then takes as an argument whatever was returned by the previous .then (however if the previous .then returned a Promise, the second .then's argument will be whatever was asyncronously resolved)
Note: if you .catch a promise that errored, and you return THAT promise, the final promise WON'T have an error, because the .catch caught it.
The best approach is to not chain that way. Instead do this:
const p1 = new Promise((resolve, reject) => {
resolve("NOT_OK_BUT_NOT_A_CATCH_NEITHER");
});
p1.then(result => {
if (result !== "OK") {
return "How do I avoid calling OK logic w/out rejecting?";
} else {
return Promise.resolve("OK")
.then(result => {
console.error("OK logic...");
});
}
})
If you're writing a linear chain, that means you're saying that you want step by step execution, which isn't what you want in this case.
Alternatively, if your target platforms/build system support it, write it as an async function:
(async function() {
const result = await Promise.resolve "NOT_OK_BUT_NOT_A_CATCH_NEITHER");
if (result !== "OK") {
return "How do I avoid calling OK logic w/out rejecting?";
} else {
await Promise.resolve("OK");
console.error("OK logic...");
}
})();
Found a way, not sure how good is it but it works. The idea is to have it as promises all the way and just not resolve when not needed.
In my case it saves me a hassle with managing ignorable results without polluting result with the likes of processable flags.
const p1 = new Promise((resolve, reject) => {
resolve("NOT_OK_BUT_NOT_A_CATCH_NEITHER");
});
p1.then(result => {
return new Promise((resolve, reject) => {
if (result === "OK") {
resolve(result);
}
// OR do nothing
console.error("Just Do nothing");
});
}).then(result => {
console.error("OK logic...");
});

Why is my Promise always hitting the catch() method?

I'm using the JavaScript Promise object with a then(), catch().
The console.log in the catch() method always runs, regardless of the response from the API ("STATUS_SUCCESS" or "STATUS_FAILED").
Is this normal behaviour in promises or is there a way to only hit the catch() method if the response has failed?
Updated with live example:
sendAccountDataToBackend(response) {
const { formData } = response;
const requestObj = {
url: 'http://localhost:3000/api/validate',
data: {
firstname: 'dummy_firstname',
lastname: 'dummy_lastname',
email: 'dummyemail'
}
};
let p = new Promise((resolve, reject) => {
account.Utils.globalAjaxRequest(requestObj, (success) => {
if(success.status === 'STATUS_SUCCESS') {
resolve();
console.log('resolved: ', p)
} else {
reject();
console.log('rejected: ', p);
}
});
})
p.then(() => {
console.log('Then: ', response);
}).catch(() => {
console.log('catch:', response);
})
}
You can find the exact cause of the thrown error by printing it.
Change your catch handler to look like this:
catch((e) => {
console.log('Catch', e);
})
In addition to "Catch" you will see a description of the error in the console.
I figured out what was causing the catch to fire thanks to #pfcodes suggestion. I was calling a function within the then() block which was failing. Once removed, it stayed inside then(). Silly mistake that was over looked! Thanks for your suggestions.
Regarding the question:
Is this normal behaviour in promises or is there a way to only hit the catch() method if the response has failed?
It is easy to demonstrate that the catch is only entered when the reject call is made.
function testPromise(shouldReject)
{
return new Promise((resolve, reject) => {
setTimeout(function(){
if(shouldReject) reject();
else resolve();
},1000);
});
}
function handlePromise(p, name){
p.then(() => {
console.log(name, 'Then');
}).catch(() => {
console.log(name, 'Catch');
})
}
var rejectPromise = testPromise(true);
var resolvePromise = testPromise(false);
handlePromise(rejectPromise,"reject");
handlePromise(resolvePromise,"resolve");
So, what this means is that in your code, for whatever reason (probably badly handled asynchronous ajax call) you're entering the else block.
Regarding your update, I would say that success.status is returning something other than "STATUS_SUCCESS" for the reasons demonstrated by the code above.
Try adding console.log(success) inside the ajax callback.
account.Utils.globalAjaxRequest(requestObj, (success) => {
console.log(success);
....

Am I chaining Promises correctly or committing a sin?

I have not worked with Javascript in a long time, so now promises are a new concept to me. I have some operations requiring more than one asynchronous call but which I want to treat as a transaction where steps do not execute if the step before failed. Currently I chain promises by nesting and I want to return a promise to the caller.
After reading the chaining section of Mozilla's Using Promises guide, I'm not sure if what I'm doing is correct or equivalent to the "callback pyramid of doom".
Is there a cleaner way to do this (besides chaining with a guard check in each then)? Am I right in my belief that in Mozilla's example it will execute each chained then even when there is an error?
myfunction(key) => {
return new Promise((outerResolve, outerReject) => {
return new Promise((resolve, reject) => {
let item = cache.get(key);
if (item) {
resolve(item);
} else {
//we didnt have the row cached, load it from store
chrome.storage.sync.get(key, function (result) {
chrome.runtime.lastError
? reject({ error: chrome.runtime.lastError.message })
: resolve(result);
});
}
}).then((resolve) => {
//Now the inner most item is resolved, we are working in the 'outer' shell
if (resolve.error) {
outerReject(resolve);
} else {
//No error, continue
new Promise((resolve, reject) => {
chrome.storage.sync.get(keyBasedOnPreviousData, function (result) {
chrome.runtime.lastError
? reject({ error: chrome.runtime.lastError.message })
: resolve(result);
});
}).then((resolve) => {
//finally return the result to the caller
if (resolve.error) {
outerReject(resolve);
} else {
outerResolve(resolve);
}
});
}
});
});
}
Subsequent then statements are not executed (until a catch) when an exception is thrown. Also, .then returns a Promise, so you don't need to create an additional, outer Promise.
Try this example:
var p = new Promise((resolve, reject) => {
console.log('first promise, resolves');
resolve();
})
.then(() => {
throw new Error('Something failed');
})
.then(() => {
console.log('then after the error');
return('result');
});
p.then(res => console.log('success: ' + res), err => console.log('error: ' + err));
You will not see "then after the error" in the console, because that happens after an exception is thrown. But if you comment the throw statement, you will get the result you expect in the Promise.
I am not sure I understand your example entirely, but I think it could be simplified like this:
myfunction(key) => {
return new Promise((resolve, reject) => {
let item = cache.get(key);
if (item) {
resolve(item);
} else {
//we didnt have the row cached, load it from store
chrome.storage.sync.get(key, function (result) {
chrome.runtime.lastError
? throw new Error(chrome.runtime.lastError.message)
: resolve(result);
});
}
}).then((previousData) => {
// keyBasedOnPreviousData is calculated based on previousData
chrome.storage.sync.get(keyBasedOnPreviousData, function (result) {
chrome.runtime.lastError
? throw new Error(chrome.runtime.lastError.message)
: return result;
});
});
}
It's a bit of a mess. This is my attempt at rewriting. A good thing to try to avoid is new Promise().
function chromeStorageGet(key) {
return new Promise( (res, rej) => {
chrome.storage.sync.get(key, result => {
if (chrome.runtime.lastError) {
rej(new Error(chrome.runtime.lastError.message))
} else {
res(result)
}
});
});
});
function myfunction(key) {
const item = cache.get(key) ? Promise.resolve(cache.get(key)) : chromeStorageGet(key);
return item.then( cacheResult => {
return chromeStorageGet(keyBasedOnPreviousData);
});
}
Why avoid new Promise()?
The reason for this is that you want to do every step with then(). If any error happened in any of the promises, every promise in the chain will fail and any subsequent then() will not get executed until there is a catch() handler.
Lots of promise based-code requires no error handlers, because promise-based functions always return promises and exceptions should flow all the back to the caller until there is something useful to be done with error handling.
Note that the exceptions to these 2 rules are in my chromeStorageGet function. A few notes here:
new Promise can be a quick and easy way to convert callback code to promise code.
It's usually a good idea to just create a little conversion layer for this callback-based code. If you need chrome.storage.sync in other places, maybe create a little utility that promisifies all its functions.
If there is only 1 'flow', you can just use a series of then() to complete the process, but sometimes you need to conditionally do other things. Just splitting up these complicated operations in a number of different functions can really help here.
But this:
const result = condition ? Promise.resolve() : Promise.reject();
Is almost always preferred to:
const result = new Promise( (res, rej) => {
if (condition) {
res();
} else {
rej();
}
}

Conditionally call a promise (or not), but return either result to another promise

having trouble trying to write the following code in a way that doesn't involve nested promises.
function trickyFunction(queryOptions, data) {
return new Promise((resolve, reject) => {
if (data) {
resolve(data);
} else {
// ... a bunch of conditions to check and/or modify queryOptions. These checks and mods
// are vital but only required if data is not passed in. ...
if (anErrorHappensHere) {
reject('Oh no, an error happened');
}
somePromise(queryOptions).then((result) => {
resolve(result);
});
}
}).then((result) => {
criticalOperation1(result);
// the code here is long and shouldn't be duplicated
});
}
I really don't like the .then() chain after somePromise since it's inside the new Promise, but I really don't see a way around it. If I take the conditional out of a promise, then I'd have to duplicate the criticalOperation1 code, which isn't an option here. The conditional checks in the else block should only happen if data is not passed in. Making other functions is not permitted in my case, and using async/await is also not permitted in my case.
Does anyone have any ideas? I've worked with Promises for a bit but this one is stumping me.
I would just avoid using the new Promise syntax in this case and just start the promise chain early
function trickyFunction(queryOptions, data) {
return Promise.resolve()
.then( () => {
if (data) {
return Promise.resolve(data);
} else {
// ... a bunch of conditions to check and/or modify queryOptions. These checks and mods
// are vital but only required if data is not passed in. ...
if (anErrorHappensHere) {
// Could also just throw here
return Promise.reject('Oh no, an error happened');
}
return somePromise(queryOptions);
}
})
.then((result) => {
criticalOperation1(result);
// the code here is long and shouldn't be duplicated
});
}
function trickyFunction(queryOptions, data) {
return new Promise((resolve, reject) => {
if (anErrorHappensHere) {
reject('Oh no, an error happened');
}
resolve({data, queryOptions});
}).then((obj) => {
if(obj.data){
return Promise.resolve(obj.data);
} else {
return somePromise(obj.queryOptions)
}
}).then((result) => criticalOperation1(result));
.catch((err)=> console.log(err));
}

Javascript - code not stoping in then

I am trying to expand my knowledge (beginner stage). Basically, I would like to use promises to write new email to my user. I have some code base in my play ground project, but my function is not stopping on then.
This is the function that should write to Database:
changeEmailAddress(user, newEmail) {
new Promise((resolve, reject) => {
user.setEmail(newEmail);
userRepository.saveUser(user).then(() => {
return resolve();
}).catch(e => {
return reject(e);
});
}
);
}
And if I am not mistaken, this is how I should use it:
changeEmailAddress(user, "hello#there.com").then(function () {
//it never comes in here :(
})
I have similar functions working on the user, but my function is not coming in to 'then'
You're committing the explicit promise constructor anti-pattern. Your code need be no more complicated than
changeEmailAddress(user, newEmail) {
user.setEmail(newEmail);
return userRepository.saveUser(user);
}
Making sure, of course, to not forget the return!
You are not returning the new Promise.
changeEmailAddress(user, newEmail) {
return new Promise((resolve, reject) => {
user.setEmail(newEmail);
userRepository.saveUser(user).then(() => {
resolve();
}).catch(e => {
reject(e);
});
});
}
You may also have an unhandled rejection.
changeEmailAddress(user, "hello#there.com").then(function () {
//it never comes in here :(
}).catch(function(e) {
console.log(e); // Does this happen?
})
EDIT: Your changeEmailAddress uses the anti-pattern (see #torazburo's answer). Although this answer works, you should just return your saveUser promise unless you want to directly work with the result of it.
your function changeEmailAddress return nothing, that's why
changeEmailAddress(user, newEmail) {
let promise = new Promise((resolve, reject) => {
user.setEmail(newEmail);
userRepository.saveUser(user).then(() => {
return resolve();
}).catch(e => {
return reject(e);
});
});
return promise;
}

Categories

Resources