I try to refacter code from using callback to Promise but I got some weird behavior from Promise(bluebird)
here is the main logic
function func1(callback) {
func2()
.then(function(resultFromFunc2) {
if (resultFromFunc2 === true) {
callback(null, resultFromFunc2)
} else {
return func3()
}
})
.then(function(resultFromFunc3) {
console.log('Func 3', resultFromFunc3)
callback(null, resultFromFunc3)
})
.catch(function(err) {
console.log('error', err)
})
}
func1(function(err, result) {
console.log('func1', err, result);
})
and in func2 and func3 is just a simple resolve
function func2() {
return new Promise((resolve, reject) => {
resolve(true)
});
}
function func3() {
return new Promise((resolve, reject) => {
resolve(true)
});
}
If func2 resolve true code should stop running in the first then, but I found the second then is called
here is result from terminal.
func1 null true
Func 3 undefined
func1 null undefined
How could I stop calling the second then when func2 is resolve true
#Phattahana,
Just posting my thoughts on your question.
As you have said in your answer, you shouldn't be calling callback() from the 1st then. Or else you should be ready to change the workflow and remove the 2nd then. there are lot many possibilities.
In a specific scenario, where you want the code to be executed just like the question you posted (ie; if the if condition is true, donot call 2nd then - scenario), you can have your code like below.
var Promise = require("bluebird");
function func1(callback) {
func2()
.then((resultFromFunc2) => {
return new Promise((resolve, reject) => {
if (resultFromFunc2 === true) {
callback(null, resultFromFunc2);
} else {
resolve(func3());
}
});
}).then((resultFromFunc3) => {
console.log('Func 3', resultFromFunc3)
callback(null, resultFromFunc3)
}).catch((err) => {
console.log('error', err)
});
}
func1((err, result) => {
console.log('func1', err, result);
return 1;
});
function func2() {
return new Promise((resolve, reject) => {
resolve(false)
});
}
function func3() {
return new Promise((resolve, reject) => {
resolve(true)
});
}
The code inside the 1st then should be made into a separate promise resolving function and should be resolving it only if the condition is not met.
Hope its clear.
Thanks,
The problem here is with your second then section. If you remove it, everything should work as expected (if I understand correctly). See the snippet below - if you change func2 to resolve(false), func3 will be called, otherwise the callback will be called.
function func1(callback) {
func2()
.then(function(resultFromFunc2) {
if (resultFromFunc2 === true) {
callback(null, resultFromFunc2)
} else {
return func3()
}
})
.catch(function(err) {
console.log('error', err)
})
}
func1(function(err, result) {
console.log('func1', err, result);
})
function func2() {
return new Promise((resolve, reject) => {
console.log('func2');
resolve(true)
});
}
function func3() {
return new Promise((resolve, reject) => {
console.log('func3')
resolve(true)
});
}
I just found the solution,
function func1(callback) {
func2()
.then(function(resultFromFunc2) {
if (resultFromFunc2 === true) {
return 'result from func2'
} else {
return func3()
}
})
.then(function(result) {
callback(null, result)
})
.catch(function(err) {
console.log('error', err)
})
}
I should not callback in the first then because whatever return from first then it send to the second then.
I miss understand behavior of Promise. first I think it will not call the second then if the first then doesn't return Promise.
Related
I often see in other peoples docs something like:
Callback is optional, if omitted returns a promise.
This is what I have:
export function doSomeAsync(options, callback) {
const useCallback = (callback && typeof callback == 'function');
const promise = new Promise((resolve, reject) => {
// --- do async stuff here ---
const check = (options.num === 1) ? true : false;
setTimeout(() => {
if (check) {
finish(true, "Number is 1");
} else {
finish(false, new Error("Number is not 1"));
}
}, 1000);
// ---------------------------
function finish(ok, rtn) {
if (useCallback) {
if (ok) {
callback(null, rtn);
} else {
callback(rtn, null);
}
} else {
if (ok) {
resolve(rtn);
} else {
reject(rtn);
}
}
}
});
return (useCallback) ? false : promise;
}
The finish() function just avoids lots of if... statements scattered around.
I'm creating a promise object, whether or not I use it.
Testing like this:
doSomeAsync({ num: 1 }).then((result) => {
console.log('p result', result);
}).catch((err) => {
console.log('p err', err);
});
doSomeAsync({ num: 1 }, (err, result) => {
if (err) {
console.log('cb err', err);
} else {
console.log('cb result', result);
}
});
This works, but I'm wondering if this is the best way, or if others have a better and more succinct implementation..?
This could be simplified if you simply always used the promise, which you're always creating anyway:
export function doSomeAsync(options, callback) {
const promise = new Promise((resolve, reject) => {
const check = (options.num === 1) ? true : false;
setTimeout(() => {
if (check) {
resolve("Number is 1");
} else {
reject(new Error("Number is not 1"));
}
}, 1000);
});
if (callback && typeof callback == 'function') {
promise.then(callback.bind(null, null), callback);
}
return promise;
}
Your function is always promise-based, also in the fact that it always returns a promise. The caller is simply free to ignore that. The callback argument is merely a "legacy fallback interface" (or "alternative interface" if you prefer) to using that promise.
You could get rid of all edge cases by always returning a promise, and define a default callback (a callback-shaped identity function) that handles the no-callback-supplied case:
const genericAsync = (stuff, callback = (e, i) => e || i) => new Promise(
(resolve, reject) => doStuffWith(stuff, resolve, reject)
)
.then(response => callback(null, response))
.catch(callback);
I created a function to check if an element already exist, if not repeat the function until it does:
function waitForElementToDisplay(selector) {
return new Promise(function (resolve, reject) {
if (document.querySelector(selector) != null) {
console.log('Element is displayed now');
resolve();
} else {
setTimeout(function () {
waitForElementToDisplay(selector, 500);
}, 500);
}
})
}
I use this function in the beforeShowPromise function of Shepherd.js. This function let the package wait with going to the next tour step until the promise is resolved. The beforeShowPromise function looks like this:
beforeShowPromise: function () {
return new Promise(async function (resolve) {
const selector = '.exampleTemplates';
await waitForElementToDisplay(selector).then(() => {
console.log('Do something');
}).catch(err => {
console.log(err);
});
})
},
I want to wait until the waitForElementToDisplay function is resolved, so the function of Shepherd can be resolved. However, the .then nor the .catch functions are being called. Can someone explain to me why it isn't working?
The Promise only resolves if the element exists.
If it doesn't, you hit the else branch which calls the function recursively. This creates a new promise, but you never do anything when that resolves. The original promise is left sitting in limbo.
You can resolve the original promise with the new promise:
resolve( waitForElementToDisplay(selector) );
You need to pass along the resolve to the recursive call:
const checkIfElementExists = (resolve, selector) => {
if (document.querySelector(selector) !== null) {
console.log('Element is displayed now');
resolve();
} else {
setTimeout(checkIfElementExists, 500, resolve, selector);
}
};
function waitForElementToDisplay(selector) {
return new Promise(function(resolve) {
checkIfElementExists(resolve, selector);
})
}
Or, encapsulated inside waitForElementToDisplay:
function waitForElementToDisplay(selector) {
return new Promise(function(resolve) {
(function checkIfElementExists() {
if (document.querySelector(selector) !== null) {
console.log('Element is displayed now');
resolve();
} else {
setTimeout(checkIfElementExists, 500);
}
})();
})
}
The issue lies in the "backendLoginCheck" function. I want to .then() chain "ifUserIsDisabled" after "getUserByEmail". But I need the "userRecord" from getUserByEmail for the input into "ifUserIsDisabled".
I also want both functions to share the same .catch in "backendLoginCheck" function.
Current Code:
function getUserByEmail(email){
return new Promise(function(resolve, reject){
firebase.serverAuthAdmin147th.getUserByEmail(email)
.then(function(userRecord) {
resolve(userRecord);
})
.catch(function (error) {
reject({token: null, errorCode: "auth/user-not-found"});
});
})
}
function ifUserIsDisabled(userRecord){
return new Promise(function(resolve, reject){
if(!userRecord.disabled){
resolve();
}
else{
reject({token: null, errorCode: "auth/user-disabled"});
}
})
}
function backendLoginCheck(email, password, callback){
var token = null;
var errorCode = null;
var uid = null;
getUserByEmail(email)
.then(function(userRecord){
ifUserIsDisabled(userRecord);
})
.catch(function(error){
callback(error);
});
}
Desired Idea:
...
getUserByEmail(email)
.then(ifUserIsDisabled(userRecord))
.then(nextFunction())
.then(nextFunction2(uses_resolveVal_from_nextFunction))
.then(nextFunctionEtc())
.catch(function(error){
callback(error);
});
If I'm understanding you correctly, it looks like you're almost there. If a then chain in a promise returns another promise you can pass the resolved values down the chain
for example:
function firstFunction() {
return new Promise((resolve, reject) => {
if (someError) {
reject(firstErr)
} else {
resolve('first value')
}
})
}
function secondFunction() {
return new Promise((resolve, reject) => {
if (someError) {
reject(secondErr)
} else {
resolve('second value')
}
})
}
firstFunction()
.then((resolvedFirstValue) => {
return secondFunction()
})
.then((resolvedSecondValue) => {
console.log(resolvedSecondValue)
})
.catch((err) => {
// any error in the entire chain
console.error(err)
})
should be like this:
getUserByEmail(email)
.then(function(userRecord){
return ifUserIsDisabled(userRecord);
})
.then(nextFunction())
I am trying to use promises for a small new project. But I have some problems with understanding how I could organize my code better, with functions. Problem is, I want my functions to handle things, and my main code to handle other things. So let's see this code:
function doSomething (isGoingToResolve = true) {
return new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
}).then(response => {
console.log("in my function",response)
}).catch(error => {
console.log("in my function",error)
})
}
doSomething().then(response => {
console.log("in my main call", response)
})
With this code, the console will log in my function something and in my main call undefined: https://jsfiddle.net/hkacvw2g/
So I found two solutions to solve my problem, but I just don't like them:
The first one is to create a variable, use a .then() on the variable, and then return the variable (https://jsfiddle.net/hkacvw2g/1/):
function doSomething (isGoingToResolve = true) {
let promise = new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
})
promise.then(response => {
console.log("in my function",response)
}).catch(error => {
console.log("in my function",error)
})
return promise
}
And the second solution (https://jsfiddle.net/hkacvw2g/2/):
function doSomething (isGoingToResolve = true) {
return new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
}).then(response => {
console.log("in my function",response)
return new Promise(resolve => {
resolve(response)
})
}).catch(error => {
console.log("in my function",error)
return new Promise((resolve,reject) => {
reject(error)
})
})
}
Am I missing a better solution to solve my problem?
You can simply return the value (or re-throw the error) and it will be resolved in promise chain:
function doSomething (isGoingToResolve = true) {
return new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
}).then(response => {
console.log("in my function",response)
return response;
}).catch(error => {
console.log("in my function",error)
throw error;
})
}
You might not want that throw error, it depends on how you want to handle your rejections. This way when you re-throw the error, you should catch it when calling the doSomething() method.
You can write a tap function, to tap into a promise chain, and do something while passing along the result, and a parallel tapCatch function, to tap into errors while rethrowing them:
const tap = fn => value => { fn(value); return value; };
const tapCatch = fn => reason => { fn(reason); throw reason; };
Now you can write your code as:
function doSomething(isGoingToResolve = true) {
(isGoingToResolve ?
Promise.resolve("something") :
Promise.reject("something else")
)
.then( tap (response => console.log("in my function", response)))
.catch(tapCatch(error => console.log("in my function", error)));
}
doSomething()
.then(response => console.log("in my main call", response));
However, actually your first solution is better. It reduces the risk of messing up the promise chain by inadvertently forgetting to, or not realizing that you have to, return the original value in then clauses, or rethrow in catch clauses which such clauses are meant only for logging purposes or other side-effects.
You could also pollute the Promise prototype with something like tap (we'll call it thenDo), making it a bit easier to use:
Promise.prototype.thenDo = function(ifFulfilled, ifRejected) {
return this.then(
value => { ifFulfilled(value); return value; },
reason => { ifRejected(reason); throw reason; });
};
Promise.prototype.catchDo = function(ifRejected) {
return this.catch(reason => { ifRejected(reason); throw reason; });
};
Now you can write
function doSomething(isGoingToResolve = true) {
(isGoingToResolve ?
Promise.resolve("something") :
Promise.reject("something else")
)
.thenDo (response => console.log("in my function", response))
.catchDo(error => console.log("in my function", error));
}
I often see in other peoples docs something like:
Callback is optional, if omitted returns a promise.
This is what I have:
export function doSomeAsync(options, callback) {
const useCallback = (callback && typeof callback == 'function');
const promise = new Promise((resolve, reject) => {
// --- do async stuff here ---
const check = (options.num === 1) ? true : false;
setTimeout(() => {
if (check) {
finish(true, "Number is 1");
} else {
finish(false, new Error("Number is not 1"));
}
}, 1000);
// ---------------------------
function finish(ok, rtn) {
if (useCallback) {
if (ok) {
callback(null, rtn);
} else {
callback(rtn, null);
}
} else {
if (ok) {
resolve(rtn);
} else {
reject(rtn);
}
}
}
});
return (useCallback) ? false : promise;
}
The finish() function just avoids lots of if... statements scattered around.
I'm creating a promise object, whether or not I use it.
Testing like this:
doSomeAsync({ num: 1 }).then((result) => {
console.log('p result', result);
}).catch((err) => {
console.log('p err', err);
});
doSomeAsync({ num: 1 }, (err, result) => {
if (err) {
console.log('cb err', err);
} else {
console.log('cb result', result);
}
});
This works, but I'm wondering if this is the best way, or if others have a better and more succinct implementation..?
This could be simplified if you simply always used the promise, which you're always creating anyway:
export function doSomeAsync(options, callback) {
const promise = new Promise((resolve, reject) => {
const check = (options.num === 1) ? true : false;
setTimeout(() => {
if (check) {
resolve("Number is 1");
} else {
reject(new Error("Number is not 1"));
}
}, 1000);
});
if (callback && typeof callback == 'function') {
promise.then(callback.bind(null, null), callback);
}
return promise;
}
Your function is always promise-based, also in the fact that it always returns a promise. The caller is simply free to ignore that. The callback argument is merely a "legacy fallback interface" (or "alternative interface" if you prefer) to using that promise.
You could get rid of all edge cases by always returning a promise, and define a default callback (a callback-shaped identity function) that handles the no-callback-supplied case:
const genericAsync = (stuff, callback = (e, i) => e || i) => new Promise(
(resolve, reject) => doStuffWith(stuff, resolve, reject)
)
.then(response => callback(null, response))
.catch(callback);