Detect if function was called with "then" [duplicate] - javascript

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

Related

How to handle both a promise or variable return from a function

Here is a simplified version of what I have:
function test(thisBool){
var response;
if(thisBool){
response = makeAPIrequest().then(function(){
}, function(err){
console.log(err)
})
} else {
response = "doesntmatter"
}
}
So if the parameter is true, make the api request. If it's false, return the 'doesntmatter' string.
When I call this, I wish to use the response in either instance.
var testResponseTrue = test(true);
var testResponseFalse = test(false);
Is there a way to do this? Or will I have to do some callback functions somehow? I'm not sure of the best way to do this, any advice would help :)
Return a promise for each condition. You already have a promise being returned from your api request method so you can use Promise.resolve() for the default
function makeApiRequest(){
// fake request ... use your normal method that already returns a prmise
return new Promise(resolve=>{
setTimeout(function(){
resolve('Value from request')
},500)
});
}
function test(thisBool){
return thisBool ? makeApiRequest() : Promise.resolve("Default value")
}
test(false).then(r => {console.log('False value:', r)});
test(true).then(r => {console.log('True value:', r)})
You should use callback or promises to handle such kind of scenarios. Below is code snippet for the same using callback:
function test(thisBool, callback) {
var response;
if(thisBool) {
makeAPIrequest()
.then(function(response) {
callback(response);
}, function(err) {
callback(err);
})
} else {
response = "doesntmatter";
callback(response);
}
}
test(true, function(testResponseTrue) {
console.log(testResponseTrue);
});
test(false, function(testResponseFalse) {
console.log(testResponseFalse);
});
It makes it a bit complicated that your consumers would have to check if they now got a promise back or not. I would rather choose to internally go for an autoresolving promise in case the boolean is false, so that you can handle the response in the same way as you would with a boolean that is true
// mock
function makeAPIrequest() {
return new Promise( (resolve) => setTimeout( () => resolve('done'), 100 ) );
}
function test(thisBool){
return new Promise( (resolve, reject) => {
if (thisBool) {
makeAPIrequest().then( result => resolve(result) ).catch( err => reject(err) );
return;
}
setTimeout( () => resolve('does not matter'), 0);
});
}
test(true).then( response => console.log('response from true = ' + response ) );
test(false).then( response => console.log('response from false = ' + response ) );
If you really want to catch the values in variables, I guess you could also use the async/await pattern, like in the following snippet
(async () => { // mock
function makeAPIrequest() {
return new Promise( (resolve) => setTimeout( () => resolve('done'), 100 ) );
}
let test = function async(thisBool){
return new Promise( (resolve, reject) => {
if (thisBool) {
makeAPIrequest().then( result => resolve(result) ).catch( err => reject(err) );
return;
}
setTimeout( () => resolve('does not matter'), 0);
});
}
var testResponse1 = await test(true);
var testResponse2 = await test(false);
console.log( testResponse1 );
console.log( testResponse2 );
})();

Async promises inside loop

I know this is a topic where there is a lot of info, but I still can't solve this.
I am working with NodeJs that connects to a mysql server, I have an array, I need to iterate the array and do some stuff with the data, the thing is I need to do a query to the mysql server but I need to the for loop wait until I got the results of the query, I have tried a lot of things, now I am trying with .each method of bluebird but still is not working properly, this is my code.
the initial function is Notification.getByUser
thanks in advance
'use strict';
var Promise = require("bluebird");
module.exports = function(Notifications) {
Notifications.execute = function(sql, itemId) {
return new Promise((resolve, reject) => {
this.dataSource.connector.execute(sql, (e, result) => {
console.log('-----RESULT----', itemId);
console.log(result);
console.log('-----ERROR-----', itemId);
console.log(e);
if (result.length === 0) {
resolve(false);
} else {
resolve(true);
}
});
});
};
Notifications.isMatching = function(item, user, type) {
return new Promise((resolve, reject) => {
console.log(type, item, user);
if (item !== null) {
if (item !== user) {
resolve(false);
}
}
resolve(true);
});
};
Notifications.getByUser = function(userId, isZolver, countryId, cityId, userState, cb) {
var where = { status: 1 };
var plainText = '';
var items = Notifications.find({ where });
Promise.each(items, item => {
return Notifications.isMatching(item.isZolver, isZolver, 'isZolver')
.then(() => {
Notifications.isMatching(item.cityId, cityId, 'cityId');
})
.then(() => {
if(item.extraCondition !== null && item.extraCondition !== '') {
var sql = item.extraCondition.replace(/:user_id/g, userId);
// console.log(sql);
Notifications.execute(sql, item.id)
.then(render => console.log('extraCondition', render));
} else {
console.log('extraCondition', true);
}
});
}).then(res => {
// console.log('res del loop', res);
});
cb(null, 'ok');
};
};
There are several issues with your code:
In order to chain promises, you must make sure to return promises that you create within a then callback
You should not need to create a promise for results that are immediately available (isMatching)
The then callback is always executed when a promise fulfils. It does not matter whether you do resolve(false): even if the promised value is false this will still make the promise fulfilled and trigger the then callback(s).
There are some unknowns in your code, like Notifications.find, and what kind of SQL you are executing: does it return a result set, if so, would you not be interested to get that result instead of just resolving to a boolean?
Anyway, here are some applied corrections:
'use strict';
var Promise = require("bluebird");
module.exports = function(Notifications) {
Notifications.execute = function(sql, itemId) {
return new Promise((resolve, reject) => {
this.dataSource.connector.execute(sql, (e, result) => {
console.log('-----RESULT----', itemId);
console.log(result);
console.log('-----ERROR-----', itemId);
console.log(e);
resolve (result.length !== 0); // The `if ... else` is overkill
});
});
};
//You don't need isMatching to return a promise
Notifications.isMatching = function(a, b) {
return a === null || a === b;
};
Notifications.getByUser = function(userId, isZolver, countryId, cityId, userState) {
var where = { status: 1 };
var items = Notifications.find({ where })
// use filter() to limit the items to those you want to execute the SQL for
.filter(item => {
return Notifications.isMatching(item.isZolver, isZolver)
&& Notifications.isMatching(item.cityId, cityId)
&& item.extraCondition !== ''
});
// Return the promise! (don't go back to the old callback system!)
return Promise.each(items, item => {
var sql = item.extraCondition.replace(/:user_id/g, userId);
// Return the promise!
return Notifications.execute(sql, item.id);
}).then(res => {
console.log('res del loop', res);
});
};
};
Note that the signature of Notifications.getByUser does not have the final callback parameter: you should keep using promises once you start with them, so when you call this function, call the result's then method like you would do for any promise:
Notifications.getByUser( .......arguments....).then( function () {
// do something...
});

Promise chaining with functions

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

If else in Promise

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.

JavaScript - Return promise AND/OR call callback?

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

Categories