I am trying to reject a value inside a nested promise but it doesn't seem to actually reject it correctly. In the code below, when I get an error from promiseVariable, it doesn't reject with the error. In the promiseVariable.catch statement, I have reject(err). Shouldn't that reject with that error for the whole promise?
return new Promise((resolve, reject) => {
const user = anotherFunction();
if (!user) {
promiseVariable.then((data) => {
user = data;
}).catch((err) => {
reject(err)
})
}
resolve(user);
});
Because it will start the promiseVariable chain and jump over to resolve. In the .then, you should resolve there, or put an else.
return new Promise((resolve, reject) => {
const user = anotherFunction();
if (!user) {
promiseVariable
.then(resolve)
.catch(reject);
} else {
resolve(user);
}
});
It seems you're over-promising, if that's a word. You can just "await" for your other promise to complete.
Here, anotherFunction is a simulated Promise, where after 3 seconds it returns a valid object for your conditional if(!user).
You can try changing resolve({user: 1234}) with a false (or falsy) value to see its rejection.
function anotherFunction() {
return new Promise((resolve,reject) => {
setTimeout(() => resolve({user: 1234}), 3000)
});
}
async function mainFunction() {
const user = await anotherFunction();
return user || {error: 404};
}
mainFunction().then(result => console.log(result));
Related
I'm reading data from db with using await so I used Promise but the function seems to return nothing
async function read() {
return new Promise((resolve, reject) => {
const db = new DB();
db
.read()
.then(result => {
resolve(result);
}).catch(() => {
reject('db-error');
});
});
}
(async () => {
const data = await read();
console.log(data); // undefined
})();
How can I make read() return result?
You are making it more complicated than it has to be. If you are already using an API that returns a promise then there is no need to use the promise constructor yourself.
And declaring a function as async is only necessary if you are using await in it to deal with promises.
So either do:
function read() {
const db = new DB();
return db
.read()
.catch(() => {
return 'db-error';
});
}
Or
async function read() {
const db = new DB();
try {
return await db.read();
} catch(error) {
return 'db-error';
}
}
If you are still not getting the value you want then you are not using the database API correctly and you have to read its documentation to figure out how to get back the right data.
The awesome guys who write the MDN Web Docs say that the result of await will be undefined if the promise that is being waited on is rejected: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#handling_rejected_promises
Check out the following scenario.
This is a simple function that returns a Promise:
function asyncFunc(waitTime) {
return new Promise((resolve, reject) => {
setTimeout(() => {
// say we prefer people who do things in 3 seconds or less
if (waitTime <= 3000) {
resolve('Promise resolved! You\'re fast! :)');
} else {
reject('Promise rejected! You\'re slow! :(');
}
}, waitTime);
});
}
Let's test the function using a method similar to yours:
async function testAsyncFunc(waitTime) {
try {
const result = await asyncFunc(waitTime);
console.log(result);
} catch(error) {
console.error(error.message);
}
}
testAsyncFunc(3000); // Returns `Promise resolved! You're fast! :)`, as expected
testAsyncFunc(3001); // Returns `undefined` instead of `Promise rejected! You're slow! :(`
But since we want the actual rejection error of the asynchronous operation instead of undefined, the solution is to chain catch to the await statement to catch any rejection errors immediately you call the asynchronous function and then throw the error so it can be caught by any catch error handler you may want to use, like so:
async function testAsyncFunc(waitTime) {
try {
const result = await asyncFunc(waitTime)
.catch(error => {
// throw the rejection error so it can be handled by the catch block below
throw new Error(error);
});
// if no errors
console.log(result);
} catch(error) {
console.error(error.message);
}
}
testAsyncFunc(3001); // Returns the expected result: `Promise rejected! You're slow! :(`
I am unable to figure out how to break a nested promise chain into a main promise chain. Here is my code :
//Main Promise chain
let promiseMain = Promise.resolve(1)
.then(result => functionA())
.then(result => nestedChain()).catch((error) => {
console.log(error);
})
.then(result => functionC())
//chain error handler
function chainError(err) {
return Promise.reject(err)
};
function nestedChain()
{
stepOne()
.then(stepTwo, chainError)
.then(stepThreee, chainError)
.catch((error) =>
{
console.log(error);
return undefined;
});
}
function stepOne()
{
return chainError("error attempt : 1.00");
}
Once I go into the nestedChain where I throw an error in stepOne() I am able to break this nestedChain. Awesome!
The problem: It also breaks the main promise chain. So.. when it goes into nestedChain() and the error is thrown from stepOne(), functionC from the main promise chain will never be executed because the rejected promise from the nestedChain also break this chain.
You will have to attach with promises chain, do something like this
function nestedChain()
{
stepOne()
.then(stepTwo, chainError)
.then(stepThreee, chainError)
.catch ((error) => {
console.log(error);
return undefined;
})
}
Promise are implemented to wait for something which take more time. You just need to implement promise correctly.
For example if we have 3 function returning promise and one nested function also returning promise, this is how it's implemented:
functionA()
.then( result => {
return functionB();
})
.then( result => {
return nestedFunct();
})
.then( result => {
return functionC();
})
.then( result => {
console.log(result);
})
.catch( error => {
console.log(`Error in one of the above function`);
});
All the function are of format similar too
function functionA() {
return new Promise( (resilve, reject) => {
resolve('something');
// or
reject('Error');
});
}
Nested function can be like this
function nestedFunc() {
return functionD() // Return promise
.then( result => {
return functionE(); // return promise
})
.then( result => {
return functionF(); // return promise or value
});
}
Main promise chain is not affected by what individual function do as long as they keep returning promise. All individual function can have a local promise chain. Even if an error occurred in nested chain will be catched by catch() in main promise chain.
If i understood you correct, you need to catch error after nested chain promise
Promise.resolve().then(()=> {
console.log('A');
}).then(() => {
return Promise.resolve().then(() => {
console.log('step 1');
}).then(() => {
console.log('step 2');
throw new Error('Step 2 error');
});
}).catch((err) => {
console.log(err);
}).then(() => {
console.log('C');
});
I am starting to use promise in NodeJS. The requirement is to use these because of async calls. Here is the first code I wrote for promise.
function asyncFunc(data) {
return new Promise(
function(resolve, reject) {
try {
resolve(data);
} catch (err) {
reject("Custom Error");
}
});
}
//Usage:
asyncFunc('Sample String')
.then(result => { console.log(result); })
.catch(error => { console.log(error); });
//Output:
Sample String
null
//If I change the code to:
function asyncFunc(data) {
return new Promise(
function(resolve, reject) {
try {
reject("Custom Error");
} catch (err) {
resolve("Data");
}
});
}
//Output:
//Exception has occurred: string
//I get the above exception at line: reject("Custom Error");
So the question is "reject" can only be used from "catch" block? Why can't I raise "reject" event manually? What is the scenario "reject" is used? Can someone provide me a better example where I can use both "resolve" and "reject"?
You can use reject if you want, of course.
Let's suppose you have a function that resolves a promise if a person is adult, and rejects it if not.
You'd have something like this:
function asyncFunc(age) {
return new Promise((resolve, reject) => {
if(age >= 18) {
resolve(true);
} else {
// here you call reject manually outside of a catch block
reject(false);
// or
// reject(Error('not adult'));
}
});
}
usage:
asyncFunc(19).then(result => {
console.log(result); // true
})
.catch(error => {
console.log(error); // false or Error('not adult')
});
Probably you got something like (node:5009) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Looks like you run your asyncFunc(..) in console. In this case node.js first execute your function after your hit Enter. Try to take your code to curly brackets like this:
{
asyncFunc('Sample String')
.then(result => { console.log(result); })
.catch(error => { console.log(error); });
}
In case of a try/catch block you usually reject at catch but you can also reject in try.
resolve and reject are basically callbacks for Promise. if you resolve it will got to the next chain if you reject it will break the chain.
So should use reject when an error occurs cause reject will break the promise chain.
For example.
Lets say you have a function that works with callback and you want to wrap it in a Promise like function. This function will check if user exists in database. If the User is found it will return true if not false and if there is an error in database (eg connection) it will reject.
function checkUserExist(id) {
return new Promise((resolve, reject) => {
checkUserExistInDatabase(id, function(result, error) {
if (error) {
reject(error);
}
if (result != null) {
resolve(true);
} else {
resolve(false);
}
})
});
}
function databaseError() {
return new Promise((resolve, reject) => {
reject();
})
}
var idsThatExist = [];
checkUserExist(1).then(function(exist) {
if (exist)
idsThatExist.push(1);
return checkUserExist(2)
}).then(function(exist) {
if (exist)
idsThatExist.push(2);
return databaseError(3)
}).then(function(exist) {
//WILL never Reach here
if (exist)
idsThatExist.push(3);
return checkUserExist(4)
}).then(function(exist) {
if (exist)
idsThatExist.push(4);
}).catch(function(err) {
//it will skip checkUserExist(4)
console.log('I got rejected after checked users:'
idsThatExist)
})
So the question is "reject" can only be used from "catch" block?
No, You can use reject anywhere. Catch isn't necessary to use reject
Why can't I raise "reject" event manually?
You can reject using creating new Promise() or static methods of Promise . See promiseUsingNew() and promiseUsingStaticMethod()
What is the scenario "reject" is used?
try/catch is used for error handling in synchronous programming. resolve & reject is for error handling in asynchronous programming operation instead of callbacks.
Can someone provide me a better example where I can use both "resolve" and "reject"?
'use strict';
function promiseUsingNew(marks) {
return new Promise((resolve, reject) => {
if (marks < 0 || marks > 100) {
return reject('Invalid marks');
}
if (marks >= 40) {
return resolve('You passed');
} else {
return resolve('You Failed');
}
});
}
function promiseUsingStaticMethod(marks) {
if (marks < 0 || marks > 100) {
return Promise.reject('Invalid marks');
}
if (marks >= 40) {
return Promise.resolve('You passed');
} else {
return Promise.resolve('You Failed');
}
}
// you can use promiseUsingNew(marks) or promiseUsingStaticMethod(marks)
promiseUsingNew(221).then((result) => {
console.log(result);
}).catch((error) => {
console.log(error);
});
I am trying to return Array of tokens stored in Firebase, and I am using 'promise'.
function getUsersTokens() {
let dbRef = db.ref('/system/users');
let result = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
let tokens = [];
snap.forEach(child => {
if(child.Status != "occupied"){
helper.getToken(child.key,db).then(function(token){
tokens.push(token);
});
}
});
resolve(tokens);
}, (err) => {
reject(err);
});
});
return result;
}
and this is the 'getToken' method from the "helper" module.
exports.getToken=function(uid,db){
return db.ref(`/Tokens/${uid}`).once('value').then(function(result){
return result.val();
});
};
The problem is that every time I push token into the array it all works fine, but when exit getUsersTokens() the array gets empty.
thanks for the help.
The issue is that your result promise is resolving too early because the helper.getToken() is non-blocking, so your forEach will finish running before all of the getToken() calls have finished pushing their token into tokens.
To make things a little easier, you can split your result promise into two promises. The first promise will be in charge of getting snap. The second promise will be in charge of iterating through snap to produce an array of tokens:
function getUsersTokens() {
let dbRef = db.ref('/system/users');
let result = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
resolve(snap);
}, (err) => {
reject(err);
});
});
return result.then(snap => {
let prommiseArr = [];
snap.forEach(child => {
if(child.Status != "occupied"){
let p = helper.getToken(child.key,db);
promiseArr.push(p);
}
});
return Promise.all(promiseArr); // resolves to array of tokens
});
}
Promise.all takes in an array of promises, and resolves when all of those promises have also resolved. the promise returned by getUsersToken will ultimately contain an array of tokens, because each promise of promiseArr resolves to a token.
It happens because the promise is resolved with the token array before getToken() resolves itself. You see an empty array because your handler runs before the tokens arrive.
You need to wait on that before resolving. Like this:
function getUsersTokens() {
let dbRef = db.ref('/system/users');
return new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
const tokensPromise = snap
.filter(child => child.Status !== "occupied")
.map(child => helper.getToken(child.key, db));
resolve(Promise.all(tokensPromise));
});
});
}
Promise.all as pointed out by #André Werlang and #Christian Santos it perfect here is an example using reduce way
function getUsersTokens() {
let dbRef = db.ref('/system/users');
let result = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
snap.reduce((chain, child) => {
return chain.then(array => {
return helper.getToken(child.key,db).then(function(token){
return array.push(token);
});
});
}, Promise.resolve([])).then(tokens=>{
resolve(tokens);
});
}, (err) => {
reject(err);
});
});
return result;
}
I want the promise catch block to not make the promise fail, I just want to return false when it doesnt pan out! How can I do this?
I tried wrapping the reject in a try catch block but it didn't work.
https://jsfiddle.net/2fz69ea2/1/
var passing_promise = new Promise(function(resolve, reject) {
resolve('Success');
});
// I want this function to just return false,
var failing_promise = new Promise(function(resolve, reject) {
reject('Failure');
})
.then(() => {
return true;
})
.catch(() => {
return false;
})
passing_promise.then(()=>{
return failing_promise()
}).then((ret) => {
console.log('please print false.. please! ', ret)
})
.catch(() => {
console.log('I Never want to make it here, but as it stands I do')
})
I also wonder if this means I am misusing promises. Please help!
Doing
.catch(() => {
return false;
})
is correct. This will resolve the promise to false.
Your problem is a typo:
return failing_promise()
failing_promise is not a function, you cannot call it. It should just be
return failing_promise
var passing_promise = new Promise(function(resolve, reject) {
resolve('Success');
});
// I want this function to just return false,
var failing_promise = new Promise(function(resolve, reject) {
reject('Failure');
})
.then(() => {
return true;
})
.catch(() => {
return false;
})
passing_promise.then(()=>{
return failing_promise
}).then((ret) => {
console.log('please print false.. please! ', ret)
})
.catch(() => {
console.log('I Never want to make it here, but as it stands I do')
})