I have a promise calling another promise but I don't know how to access the variable memberContractInfo where I am trying to store all of the promises. In the below code I have 2 questions labeled QUESTION 1 and QUESTION 2.
export function sendRequestAndLoadResponseForAllMemberContractInfo() {
return function sendRequestAndLoadResponseForAllMemberContractInfoThunk(dispatch) {
dispatch(getRequestsAction());
return returnPromiseWithAllMemberContracts()
.then(promiseWithAllMemberContracts => {
// Step 1) get all member ids in response
let contracts = promiseWithAllMemberContracts.response.contract;
let memberContractInfo = []; // <==== I want to store result of all 2nd promises here
for (let i in contracts) {
const memberID = contracts[i].member_id;
returnPromiseWithAllMemberInfo(memberID)
.then(secondAPICallResponse => {
// Step 2) make 2nd API call using memberIDs as parameter
memberContractInfo.push(secondAPICallResponse);
console.log('secondAPICallResponse = ', secondAPICallResponse);
if (memberContractInfo.length === 2) {
console.log('memberContractInfo.length = 2');
// QUESTION 1: I can access memberContractInfo here but I there must also be
// another place I can access it right?
}
})
}
console.log('memberContractInfo = ', memberContractInfo); // <== QUESTION 2: Why is this empty?
});
}
}
function returnPromiseWithAllMemberContracts() {
return fetchData('/api-proxy/contract/contract');
}
function returnPromiseWithAllMemberInfo(memberID) {
let servicePath = '/api-proxy/member?id='.concat(memberID);
console.log('fetchData(', servicePath);
return fetchData(servicePath);
}
You can access memberContractInfo anywhere inside they scope it is declared in then(promiseWithAllMemberContracts => {}.
memberContractInfo is empty in console.log('memberContractInfo = ', memberContractInfo); because you reach this statement before you actually resolved the promise.
As mentioned by #bergi you need to use Promise.all instead of loop.
Promise.all(contracts.map((contract) => {
return returnPromiseWithAllMemberInfo(contract.member_id);
})).then(values => {
// values has response from all the above API calls stored in it
return values;
}).then((memberContractInfo) => {
console.log(memberContractInfo.length);
// this will give you correct value.
})
Related
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.
I have a function which has nested Promises within it. I want to re-execute this function when there is a length value of streamRes.length. Currently, it only executes the code and prints out the length on the console but doesn't re-execute.
let fetchEnrolleesData = () => {
getLastUpdatedEnrollee().then(res => {
let params = path+"enrollees?limit=100";
params += res.last_date ? "&last_updated_at=" + res.last_date.value : '';
fetchEnrolleesDataInStream(res, params).then(streamRes => {
if(streamRes.length) {
console.log(streamRes.length);
fetchEnrolleesData();
}
});
});
}
As evolutionxbox wrote, you need to return the promise returned by getLastUpdatedEnrollee as well as the promise getLastUpdatedEnrollee itself. In general, when calling a promise within a function, you need to return those promises in order to use those values outside of that function.
Here is your code with return added as needed:
let fetchEnrolleesData = () => {
return getLastUpdatedEnrollee().then(res => {
let params = path+"enrollees?limit=100";
params += res.last_date ? "&last_updated_at=" + res.last_date.value : '';
return fetchEnrolleesDataInStream(res, params).then(streamRes => {
if(streamRes.length) {
console.log(streamRes.length);
return fetchEnrolleesData();
}
});
});
}
As an optional side note, you may or may not prefer to use () => … syntax instead of () => { … } to remove the need to write return in the top-level function. You can do that in the top-level function because it has no other statements; this wouldn’t work in the inner function, which has multiple statements. See arrow function expressions for more detail.
let fetchEnrolleesData = () =>
getLastUpdatedEnrollee().then(res => {
// …
});
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
Hi I'm having a question related to java script Promise.
var value;
Promise.resolve(await this.triggerAPI_MDBREADMI_GetFGLINHU2()).then(res => {
if (a === 1) {
RequestSearchController.runNumber = res;
} else {
RequestSearchController.runNumber = RequestSearchController.runNumber + 1;
}
value = RequestSearchController.runNumber;
});
console.log(value);
I need to assign the RequestSearchController.runNumber to an external variable, 'value'. But my results are undefined. can someone help me to identify the issue that I have made
Value is defined.
It's just being logged before a value is assigned to it. the promise is asynchronous, meaning the console.log will be triggered before the code in the promise is executed, resulting in show it's undefined value. Value is defined after the promise resolves, you just logged it before it became that. Put the console.log IN the promise callback at the bottom and you'll log it after it's been defined.
Anitpatterns
When you're using async functions, the await keyword pauses the function from executing until the promise after the await resolves. Therefor, you don't need to wrap it in a Promise.resolve or use the .then() pattern.
Fixed code:
var value;
var res = await this.triggerAPI_MDBREADMI_GetFGLINHU2())
RequestSearchController.runNumber = (a === 1 ? res : RequestSearchController.runNumber +1);
value = RequestSearchController.runNumber;
console.log(value);
Is RequestSearchController undefined? If so that would be a separate API problem.
If you are getting a valid response, simply put the console.log inside of a .then. Also you dont need await
var value;
Promise.resolve(this.triggerAPI_MDBREADMI_GetFGLINHU2()).then(res => {
if (a === 1) {
RequestSearchController.runNumber = res;
} else {
RequestSearchController.runNumber = RequestSearchController.runNumber + 1;
}
value = RequestSearchController.runNumber;
})
// New code here v
.then( () => console.log("value", value))
inside a for loop/ return value from a function:
var value;
myFunc = () => {
for (var i = 0; i < something; i++) {
Promise.resolve(this.triggerAPI_MDBREADMI_GetFGLINHU2()).then(res => {
if (a === 1) {
RequestSearchController.runNumber = res;
return RequestSearchController.runNumber;
} else {
RequestSearchController.runNumber = RequestSearchController.runNumber + 1;
return RequestSearchController.runNumber + 1
}
})
// New code here v
.then( res => value = res)
}
console.log(value) // value only prints once
return value
}
I've made a promise based function which crawls up a hierarchy until it reaches the top, and resolves with an object containing the structure. My only gripe with the code is that I modify variables outside the function body, meaning that it is not a pure function. I've looked into JavaScript closures, and I fully grasp trivial uses of them. But I'm struggling to figure out how/if they can help make my function pure. My attempts at making a closure so far have only overwritten the variables, not modified them. Here is the code in question using global variables:
/* I want to move these variables inside function body to purify 'getPriorRows'*/
let priorRows = {}, level = 0;
const getPriorRows = id => new Promise(resolve => {
fetch(`/api/org/${id}`).then(result => {
/* global varaiables are modified here */
priorRows[level++] = result;
if (result.parentID) resolve(getPriorRows(result.parentID));
else resolve(priorRows);
});
});
getPriorRows('123432').then(result => console.log(result));
Any input on the matter is greatly appreciated.
Pass the values as arguments:
function getPriorRows(id, priorRows = {}, level = 0) {
return fetch(`/api/org/${id}`).then(result => {
/* global varaiables are modified here */
priorRows[level] = result;
if (result.parentID) return getPriorRows(result.parentID, priorRows, level+1);
else return priorRows;
});
}
getPriorRows('123432').then(result => console.log(result));
You can use either default parameters or a wrapper function, you don't even need a closure:
function getAll(id) { return getPriorRows(id, {}, 0); }
Also the I removed the Promise constructor antipattern.
You should be able to enclose the entire function and its "external" variables in a new function:
function getPriorRows(id) {
let priorRows = {}, level = 0;
const getNext = id => new Promise(
...
);
return getNext(id);
}
That said, your creation of an explicit new Promise in each iteration is a Promise anti-pattern:
function getPriorRows(id) {
let priorRows = {}, level = 0;
const getNext = id => fetch(`/api/org/${id}`).then(result => {
priorRows[level++] = result
if (result.parentID) {
return getNext(result.parentID));
} else {
return priorRows;
}
});
return getNext(id);
}
Either way, the advantage of wrapping the state like this is that you could now have multiple calls to getPriorRows proceeding in parallel without interfering with each other.
EDIT second code edited to fix a copy&paste error with the recursion - you must call the inner function recursively, not the outer one.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
I have a function which modifies a variable as per response from database.
function f () {
let x = { };
redis.get ("key1").then ( value => x.prop1 = value);
redis.get ("key2").then ( value => x.prop2 = value);
return x;
}
This is my code roughly. It is actually in a for loop, using node-redis.
I have multiple calls to Redis. I add the data from Redis to a new object and return the object.
PROBLEM: function f () is returning an empty object every time I call it.
How do I wait for all Redis promises to resolve before returning the object?
In your code, you are not waiting for redis values to resolve before you are returning x. You should enclose it in async-await for that.
Also, note that invoking f() will not return the value directly, it will return a promise.
async function f () { // note async
let x = { };
await redis.get ("key1").then ( value => x.prop1 = value); // note await
await redis.get ("key2").then ( value => x.prop2 = value);
return x;
}
You can then use it like f().then(console.log)
Example:
//var Promise = require('bluebird')
console.log("started");
var f = async function() {
return {
prop1: await Promise.delay(3000).then(_ => 3),
prop2: await Promise.delay(2000).then(_ => 4)
}
}
f().then(console.log)
<script src="https://cdn.jsdelivr.net/bluebird/3.5.0/bluebird.js"></script>
which can also be simplified to:
async function f () {
let x = { };
return { prop1: await redis.get('key1'), prop2: await redis.get('key2') }
}
Edit: On thinking about this more, I think you should just rather move away from async-await for this use-case, and instead use Promise.all. Reason - those two redis get operations can happen concurrently and shouldn't wait for it's turn. Using await will stall the invocation of second redis call. Try running the example snippet above, and you will notice how the result comes in total 5s (3 + 2), while it easily come in ~3s.
Promise.all accepts a bunch of promises and waits for all of them to resolve before invoking it's resolution.
console.log("started");
var f = function() {
var x = {}
var promises = [
Promise.delay(3000).then(_ => { x.prop1 = 3 }),
Promise.delay(2000).then(_ => { x.prop2 = 4 })
]
// wait for all of above to resolve and then resolve with `x`
return Promise.all(promises).then(_ => x)
}
f().then(console.log)
<script src="https://cdn.jsdelivr.net/bluebird/3.5.0/bluebird.js"></script>
^ Try running this snippet now and note the difference in time.