Conditionally chaining promises inside and outside of a loop in javascript - javascript

In the following code, when the condition within the loop is satisfied we can't really be sure in which order are both asyncFunction called.
siblings.forEach(function(sibling, index) {
// This condition may only be satisfied once (only one sibling may have data-selected true)
if (sibling.getAttribute('data-selected') == 'true') {
asyncFunction(sibling);
}
})
asyncFunction(element);
Since I know asyncFunction() returns a promise I could chain those to ensure the order:
asyncFunction(sibling).then(asyncFunction(element));
But how to do this taking the condition into account ?
I considered wrapping the loop in a promise that resolves when the condition is satisfied or after the loop ends, but it seems a tad convoluted.
// Untested
function checkSiblings(siblings) {
let promise = new Promise(function(resolve, reject) {
siblings.forEach(function(sibling, index) {
if (sibling.getAttribute('data-selected') == 'true') {
resolve(asyncFunction(sibling));
}
})
resolve();
})
}
checkSiblings(siblings).then(asyncFunction(element));
I am sure this has been addressed before, but I can't find the right search keyboards.
Thank you

If only one of the siblings can meet the criteria, using find will simplify your code:
const selected = [...siblings].find( elem => elem.getAttribute('data-selected') == 'true' );
Mark checkSiblings as async so it always returns a Promise, and have it return the asyncFunction Promise when appropriate:
return selected ? asyncFunction(selected) : null;
Then you can chain them as before:
checkSiblings(siblings).then(() => asyncFunction(element));
All together you’d have something like this:
async function checkSiblings (siblings) {
const selected = [...siblings].find( elem => elem.getAttribute('data-selected') == 'true' );
return selected ? asyncFunction(selected) : null
}
checkSiblings(siblings).then(() => asyncFunction(element));

There are several ways. One of them is using Array.reduce to resolve promises sequentially:
[...siblings].reduce(
async (prev, sibling)) => {
// Await initial/previous promise (discard results)
await prev;
// This condition may only be satisfied once
// (only one sibling may have data-selected true)
if (sibling.getAttribute('data-selected') == 'true') {
// Return the async function promise
return asyncFunction(sibling);
}
// async functions always returns a promise.
// No need to return anything
// Initial promise autoresolved
}, Promise.resolve())
// Once all have been executed
.then( () => asyncFunction(element));

Related

Javascript: Recursive function with promise and resolve when count is reached

I am stuck trying to build a recursive function that is already defined as a promise.
I have not been able to apply the recursive pattern on the code below which is looping only once even though loopFor is initialised at 20 what I am missing?
Requirement: receivingMessages must be a promise.
let globalMessageArray = [];
let count = 0;
let loopFor = 20;
function receivingMessages(params, loopFor, globalMessageArray) {
return new Promise((resolve, reject) => {
const command = new ReceiveMessageCommand(params);
client.send(command).then(
(data) => {
if (data && data.Messages && data.Messages.length) {
data.Messages.forEach(msg => {
globalMessageArray.push(msg);
});
};
return resolve(globalMessageArray);
},
(error) => {
return reject(error);
}).then(
(globalMessageArray) => {
count = count + 1;
console.log("Loop Count: " + count); // always returns 1
if (loopFor === 1) {
return resolve(globalMessageArray);
} else {
return resolve(receivingMessages(params, loopFor - 1, globalMessageArray));
};
});
});
};
In the first then callback client.send(cmd).then(data => … you return resolve(globalMessageArray). This effectively short-circuit your loop, because a promise can only resolve once. Later call of resolve has no effect.
client.send(cmd).then((data) => {
…
return globalMessageArray;
}, …
Remove first call to resolve should solve your problem.
You said in comment:
Using async/await would imply to rewrite the whole program
No, your understanding of async/await is wrong. Any async function is automatically a promise returning function, which meets your requirement. Async/await is just syntax sugar on top of promise.
This means you can safely rewrite ONLY receivingMessages function without needing to modify other places that call it.
Although there is nothing wrong with vanilla promise, rewriting to async/await will make your code so much cleaner.
async function receivingMessages(params, loopFor, globalMessageArray) {
const command = new ReceiveMessageCommand(params);
const data = await client.send(command);
if (data && data.Messages && data.Messages.length) {
data.Messages.forEach(msg => {
globalMessageArray.push(msg);
});
}
if (loopFor === 1) {
return globalMessageArray;
} else {
return receivingMessages(params, loopFor - 1, globalMessageArray)
};
};
The issue with your code is that the resolve call in the then callback after the client.send promise resolves is returning the result of calling receivingMessages instead of the receivingMessages promise itself. This causes the recursive loop to only execute once.
To fix this, you can change the resolve call to return the result of calling receivingMessages directly:
return receivingMessages(params, loopFor - 1, globalMessageArray);
This will cause the receivingMessages function to be called in a recursive manner until loopFor reaches 1.
You may also want to consider adding a base case to the function to ensure that it terminates, such as adding a check for loopFor being less than or equal to 0 and returning the globalMessageArray in that case.

Promise chain works every time even though the return statement is conditional

I recently restructured (flattened) this chain. However I am confused as to why this chain still works in any case even though the return statement in one of the .then links is conditional:
function addList(name) {
let listObj = {};
listObj.name = name;
return nameExists(name) //returns a promise<boolean>
.then((result) => {
console.log("foo");
return result; //without this return statement the chain would break,
//which makes sense to me because it does not return a
//promise otherwise.
})
.then(bool => {
listObj.unique = !bool;
if (validListID(name)) { //this is a synchronous regex function
listObj.unique = false;
}
if (!listObj.unique)
return Counters.getNewShortListId(); // returns Promise
//Does not return promise when condition not met.
//However the next chain gets called in any case.
})
.then((id) => { //this gets called even when listObj.unique = true,
//also this works perfectly, why?
listObj.__id = id;
return new List(listObj).save();
});
}
I am really confused as to why this behaves the way it does. I thought the promise chain breaks when no promise is returned?
If you don't return a Promise from a .then, the next .then will continue to work just fine (since no error was thrown), but the parameter for its callback will always be undefined:
Promise.resolve()
.then(() => {
// don't return anything
})
.then((param) => {
console.log(param);
});
If you're sure that getNewShortListId, if, when it resolves, always resolves to a value that is not undefined, simply check that the next .then's id is not undefined.
.then((id) => {
if (id !== undefined) {
listObj.__id = id;
return new List(listObj).save();
}
});
Another option is to have the upper .then create the Promise instead of having the lower .then:
if (!listObj.unique)
return Counters.getNewShortListId()
.then((id) => {
listObj.__id = id;
return new List(listObj).save();
})
If you don't like the look of the nested .thens, you could convert the nested .then callback to a named function that you declare beforehand (eg const processId = (id) => ...).
You can also throw an error (eg if (listObj.unique) throw new Error()) to completely break out of the current .then chain, and control flow will immediately go to the next .catch, skipping intermediate .thens - but errors generally shouldn't be used for control flow.

Java Script Promises - If Statement Return Best Practices

I'm writing a node.js function that returns a different promise depending on a condition, the cod:
if(condition){
return promise.then(() => {
return Promise.resolve(value)
})
}else{
return anotherPromise
}
Now the problem is that if the condition is true, I need to something after the promise is fulfilled, but in the other case I just return the promise, so the eslint tells me that it's a bad practice to nest promises. So this code won't work for me:
(() => {
if(condition){
return promise
}
}else{
return anotherPromise
}
}).then(() => {
return Promise.resolve(value)
})
Because using this code the then callback will be executed in the two cases.
What is the best practice to handle this case?
If you use classic (ES6 / ES2015+) Promise syntax you have to chain promises (nothing bad with it!).
But you have also option to split the code into functions to gain readability and avoid nesting issues:
const firstCase = () => ... // returning a promise
const secondCase = () => ... // returning a promise
if (condition) {
return firstCase()
} else {
return secondCase()
}
But with ES7/ES2016+ you can use async/await syntax:
// in a "async" function
async function main() {
if (condition) {
await promise // if you need the promise result, you can assign it
return value // the result of an async function is always a Promise.
} else {
return anotherPromise
}
}
or mix both solutions.
A simple suggestion, (this should work) pass the condition in the resolve's argument and check it in the then block. The pseudo-code below will clarify it better:
(() => {
if(condition){
return new Promise((resolve,reject)=>{
//Some tasks
resolve(condition)
//Some reject condition
reject()
})
}
else {
return new Promise((resolve,reject)=>{
//Some tasks
resolve(condition)
//Some reject condition
reject()
})
}
}).then((condition) => {
if(condition) {
//Do something
}
else {
//Do Nothing
}
})
eslint tells me that it's a bad practice to nest promises.
Just tell it to shut *** up disable the linter on this statement. Promises have the ability to be nested precisely so that you can nest them when you need it, and this is one of those cases. Your code is fine.
Seems like you are over complicating it. The then method already returns a promise so you don’t need to put a Promise.resolve inside it.
You can for simplicity do
return condition
? promise.then(() => value)
: anotherPromise

Async function with both sync and async code

I have async function that returns true or false. It looks like following one:
class User {
async canManageGroup(group) {
if (typeof group === 'number') {
// group - id
group = await getGroupById(group)
} // else group is already loaded from DB
return this.id === group.manager.id
}
}
If group parameter is ID of group then function will make async call to DB, so function canManageGroup will execute asynchronously. But if group parameter is group model then function will only call return this.id === group.manager.id or it will execute syncronously. Is it good practice to write code in this way? Or I should transform synchronous code to asynchronous?
function makeAsync(cb) {
return new Promise(resolve => setImmediate(() => resolve(cb())))
}
class User {
async canManageGroup(group) {
if (typeof group === 'number') {
// group - id
group = await getGroupById(group)
} // else group is already loaded from DB
return await makeAsync(() => this.id === group.manager.id)
}
}
You can use the first example without problems.
When you use async, your function will return a Promise. If your code is sync, the status of the returned promise will be resolved and it is safe to run any promise-related code on it (then, catch, etc).
For example:
async function truePromise() {
return true;
}
truePromise().then(function(value){
console.log("Promise value:", value);
});
Just works :)
Should you do it?
Yes. It is okay and works okay thanks to the async keyword.
What you MUST NOT do is the following:
function dontDoIt(doSync) {
if (doSync) return false;
return Promise.resolve(false)
}
Why? Because:
if doSync is truhtly, it will return false (i.e a boolean)
if doSync is falsy, it will return a Promise that will resolve to false.
That is a HUGE difference.
Why?
Your function sometimes returns a promise, and other times return a boolean. It is inconsistent.
doSync(true) is a boolean, you can't use await nor .then.
doSync(false) is a Promise, you can use await and then.
Based on your comment to one of the answers you are concerned because of the answer to the question How do you create custom asynchronous functions in node.js?
The problem is, this function is inconsistent: sometimes it is asynchronous, sometimes it isn't. Suppose you have a consumer like this:
In js enviroments like nodejs is common practice that the callback that could be executed async, should always be called async, no matter if the actual code was really async.
So you are wondering if the following code will break that common practie.
async function doSomething() {
return true
}
doSomething()
.then(res => {
console.log(res)
})
This is not the case, because here you deal with Promises and a Promise is allowed to be resolve immediatly, the code above is basicly the same as if you would write:
Promise.resolve(true)
.then(res => {
console.log(res)
})
The async behaviour is ensured in the chaining part (then/catch), the callback you pass to the then/catch will be called async:
async function doSomething() {
return true
}
console.log('before');
doSomething()
.then(res => {
console.log('with in callback')
})
console.log('after');
As you can see the order of the logs is:
before
after
with in callback
So this is in the same order you would expect from an regular async callback function.
The first one is correct. There is no need to force anything to async if there doesn't need to be. Context switching isn't free, so if it doesn't have to wait for anything to complete, don't try and make it.

While loop using bluebird promises

I am trying to implement a while loop using promises.
The method outlined here seems to work.
http://blog.victorquinn.com/javascript-promise-while-loop
it uses a function like this
var Promise = require('bluebird');
var promiseWhile = function(condition, action) {
var resolver = Promise.defer();
var loop = function() {
if (!condition()) return resolver.resolve();
return Promise.cast(action())
.then(loop)
.catch(resolver.reject);
};
process.nextTick(loop);
return resolver.promise;
};
This seems to use anti-patterns and deprecated methods like cast and defer.
Does anyone know a better or more modern way to accomplish this?
Thanks
cast can be translated to resolve. defer should indeed not be used.
You'd create your loop only by chaining and nesting then invocations onto an initial Promise.resolve(undefined).
function promiseWhile(predicate, action, value) {
return Promise.resolve(value).then(predicate).then(function(condition) {
if (condition)
return promiseWhile(predicate, action, action());
});
}
Here, both predicate and action may return promises. For similar implementations also have a look at Correct way to write loops for promise. Closer to your original function would be
function promiseWhile(predicate, action) {
function loop() {
if (!predicate()) return;
return Promise.resolve(action()).then(loop);
}
return Promise.resolve().then(loop);
}
I prefer this implementation as its easier to simulate break and continue with it:
var Continue = {}; // empty object serves as unique value
var again = _ => Continue;
var repeat = fn => Promise.try(fn, again)
.then(val => val === Continue && repeat(fn) || val);
Example 1: stops when either the source or the destination indicate an error
repeat(again =>
source.read()
.then(data => destination.write(data))
.then(again)
Example 2: stop randomly if the coin flip given 90% probability results with a 0
var blah = repeat(again =>
Promise.delay(1000)
.then(_ => console.log("Hello"))
.then(_ => flipCoin(0.9) && again() || "blah"));
Example 3: Loop with condition that returns the sum:
repeat(again => {
if (sum < 100)
return fetchValue()
.then(val => sum += val)
.then(again));
else return sum;
})

Categories