array Length not reporting - javascript

This should be simple, but it doesnt make sense to me.
It is a simple section of a promise chain
let flightName = [];
let guidArr = [];
Promise.all(guidArr)
.then(values => {
for(var a = 0; a < values.length; a++) {
Xrm.WebApi
.online
.retrieveRecord("crd80_flightplanevent", values[a], "?$select=_crd80_learnerflight_value")
.then(
function success(result) {
flightName.push(result["_crd80_learnerflight_value#OData.Community.Display.V1.FormattedValue"])
},
function(error) {
DisplayError(error)
});
}
return flightName
}).then(flightName => {
console.log(flightName)
console.log(flightName.length)
return flightName
})
The console displays the flightName array correctly,
but flightName.length is always consoled as 0, even though console.log(flightName) puts length:2 as the output
Why???
I need to work with each item in the array but it is not recognised correctly

The recommendation by #derpircher worked:
You are not awaiting the promises in your for(var a = 0; a < values.length; a++) loop. Thus, when you do return flightName the array is still empty, and thus console.log(flightName.length) prints 0. The output from console.log(flightName) might pretend, that flightName contains values, but actually it does not because that console.log is referencing the object, which is later filled when the promises resolve. Do console.log(JSON.stringify(flightName)) and you will see it prints just [] because at that moment, the array is still empty
To resolve that issue, make the first then handler async and use await Xrm.WebApi... or properly wrap that whole thing in a Promise.all

Related

Promise on forEach loop which contains promises

I have nested forEach loops out of which one of them contains another await call. I want my function doSomething to be executed only after the nested loop is done. As of now doSomething() gets executed first.
entity.forEach(function (dataItem) {
userParams.forEach(async function (userParamItem) {
const Marks = await _fetchMark(req, dataItem.MKTUNITID);
//do some manipulation to Marks
}
}
});
});
doSomething();
Whenever I have to use awaits inside a forEach (or any iterator loop; for, while, array-methods), I like to create a little array where I store the promises that are made.
I'll type out an example. I have a list of 10 urls I want to run a fetch towards, and store the results before running doSomething(). My runFetch()-function is asynchronous, and thus by default (unless I await it) returns a promise. After iterating, I will await ALL the promises at the same time, so they can run in parallell, but still wait for the slowest one.
const urls = [...] // my urls
const promises = []
for (const url of urls) {
promises.push( runFetch(url) )
}
const results = await Promise.all(promises)
//now that all my fetches are done, I can continue.
doSomething()
forEach can be bit tricky in such situations, you can simply use for loops instead as
they will not jump to next item until all promises are not resolved
for(i =0; i < entity.length; i++)
{
let dataItem = entity[i]
for(j =0; j < userParams.length; j++)
{
let userParamItem = userParams[i]
const Marks = await _fetchMark(req, dataItem.MKTUNITID);
//do some manipulation to Marks
}
}
Javascript does this because forEach is not promise-aware. It cannot support async and await so you cannot use await in forEach. Instead, use a for...of loop:
for(const dataItem of entity) {
for(const userParamItem of userParams) {
const Marks = await _fetchMark(req, dataItem.MKTUNITID);
//do some manipulation to Marks
}
}
});
doSomething();
This way the nested loops will execute first and doSomething() last.
Here is a nice post if you wanna learn more:
https://zellwk.com/blog/async-await-in-loops/

Can we pass parameters to a generator when we iterate it via for..of?

I am thinking about a scenario of building up a promise queue:
//Let's assume that promises is an array of promises
var promiseQueue = [];
for (var promise of promises) {
if (promiseQueue.length) promiseQueue[promiseQueue.length - 1].then(promise);
promiseQueue.push(promise);
}
I am thinking about implementing a function called resolver:
function *resolve() {
var promise;
while (promise = yield) Promise.resolve(promise);
}
and then iterating it:
var promiseGenerator = resolve();
The problem is the for..of here which would be responsible for the actual iteration:
for (var r of promiseGenerator) {
}
At the code above the generator will be successfully iterated, but unfortunately I am not aware of a way to successfully pass a parameter to this generator at the iteration of for..of.
I would like to clarify that I do not need an alternative, I am perfectly aware that we can do something like this:
for (var p in promiseQueue) promiseGenerator.next(promiseQueue[p]);
I am specifically interested to know whether I can pass parameters to the generator when I execute a for..of cycle.
EDIT
The problem raised by amn is that in the example he/she was focusing on would always get undefined. That's true if we pass undefined to next(), but not true if we pass something else. The problem I was raising is that a for..of loop does not allow us to pass anything to yield, which is this specific question is all about, the example is a mere illustration of the problem, showing that the promises we would create will never be created in a for..of loop. However, there is life for Iterable objects outside the realm of for..of loops and we can pass defined values into the yield. An example with the criticized code chunk can look like:
function *resolve() {
var promise;
while (promise = yield) Promise.resolve(promise);
}
var responses = [];
var f = resolve();
var temp;
for (var i = 10; !(temp = f.next(i)).done; i--) responses.push(temp);
As we can see above, the yield above cannot be assumed ab ovo to be undefined. And of course we can pass some custom thenables, like
Promise.resolve({
then: function(onFulfill, onReject) { onFulfill('fulfilled!'); }
});
or even promises which were not resolved yet. The point of the example was to show that we cannot pass values to the yield using the for..of loop, which is quite a feature gap in my opinion.
No, it is not possible to pass arguments to next.
function* generateItems() { /* ... */ }
for (var item of generateItems()) {
console.log(item);
}
is mostly short for
function* generateItems() { /* ... */ }
var iterator = generateItems()[Symbol.iterator]();
do {
const result = iterator.next();
if (result.done) break;
const item = result.value;
console.log(item);
} while (true);
barring a few missing try/catch wrappers. You can see in the spec here that it calls .next with no arguments:
Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « »).
e.g.
iterator.next.apply(iterator, []);
calling next() with an empty array of arguments.

Javascript - How to return object with promise

I'm trying to return an object with a specific structure but i can't return the object.
As you can see in the code if i print inside promise it prints data but if i print outside is empty... why?
How can i return the object parts?
var parts = [];
var part = [];
var articles = [];
part.push(request.query("SELECT u_order, u_familia, u_part, u_type FROM u_part (nolock) where u_order <'92' and u_order <> '100'"));
return Promise.all([Promise.all(part)]).then(function(listOfResults)
{
for(var i=0; i<listOfResults[0][0].length; i++)
{
articles.push(request.query("SELECT st.u_posic, sc.ref, sc.qtt, sc.design FROM st INNER JOIN sc ON st.ref = sc.ref where sc.ststamp ='"+stamp+"' and st.u_posic = '"+listOfResults[0][0][i].u_order+"'"));
//articles.push(request.query("SELECT ststamp, ref, design FROM st (nolock) WHERE u_posic = '"+listOfResults[0][0][i].u_order+"'"));
Promise.all([Promise.all(articles)]).then(function(listOfArticles)
{
for(var j=0; j<listOfResults[0][0].length; j++)
{
parts.push({
u_order: listOfResults[0][0][j].u_order,
u_familia: listOfResults[0][0][j].u_familia,
u_part: listOfResults[0][0][j].u_part,
u_type: listOfResults[0][0][j].u_type,
articles: listOfArticles[0][j-1]
});
console.log(parts); HERE THE OBJECT HAD DATA
}
});
}
console.log(parts); BUT HERE IS EMPTY
}).catch(function(err)
{
console.log(err);
});
Thank you.
The whole point of Promises is being asynchronous. You need to wait for your Promise to resolve or reject to check for data. Specifically, you should only do anything with the returned value of your Promise within the Promise.then() method, which runs as soon as the Promise finishes execution. Changing variables which exist outside the Promise from within it, as you are doing by calling parts.push() is a bad practice. You should return parts within your promise, and then, in a final .then() callback, you should do whatever is required with parts.
Something like:
Promise.all(part).then(makeAllQueries).then(doSomethingWithQueryResults);
You can only be sure you actually have data or not when your final .then callback runs.
Also, doing Promise.all([Promise.all(part)]) does nothing for you, as you're just wrapping your returned array in one more array level.

Array undefined outside of javascript function?

I've defined a function in React-Native that gets an object from Firebase and pushes its contents onto an array. However, although the array is being populated fine after my call to 'push', it is undefined at the end of the for loop. I have no idea why it's being cleared, as I'm defining the array outside of the for loop, so I thought I'd ask for an extra pair of eyes in case I'm missing anything.
var newList = [];
var arrayLength = userArray.length;
for (var i = 0; i < arrayLength; i++) {
var userRef = usersRef.child(userArray[i]);
userRef.once('value', function(snap) {
newList.push({
name: snap.val().name,
description: snap.val().description,
});
//this log statement prints out correct array
console.log("NEWLIST" + newList[i]);
});
//but array is undefined here
console.log("NEWLIST 2" + newList[i]);
}
this.setState({
current: this.state.current.cloneWithRows(newList),
past: this.state.past.cloneWithRows(oldList)
});
The code looks fine but what I think might be happening here is that userRef.once() is an async call, and your outer console.log is getting executed first. And when the event triggers the callback your inner console.log() prints the value
Read up on JavaScript's asynchronous nature. The once function takes a callback. The console.log in the outer scope is executed before you push any items info your array.

Enclosed return value and promises

I have ran out of ideas dealing with this nested return situation.
I am trying to find out if the array retrieven from the backend via a promise contains an element in the array.
This works:
function isNameTaken() {
var duplicateFound = 0;
for (var i = 0; i < $scope.entities.length; i++) {
if ($scope.entities[i].name === $scope.myName) {
duplicateFound = 1;
}
}
return duplicateFound;
}
I'd like to adjust this so there is a refresh of the array before the comparison. I have a method for retrieving it that resolves into a promise. Now, I can wrap the whole thing into a .then wrap but I then cannot access my duplicateFound variable from outside to the the async nature of the inner function.
This what I want to get working:
function isNameTaken() {
return loadEntities().$promise.then(function(entities) {
var duplicateFound = 0;
for (var i = 0; i < entities.length; i++) {
if (entities[i].name === $scope.myName) {
duplicateFound = 1;
}
}
return duplicateFound;
});
}
The inner part works flawlessly as the promise resolves, but the function as a whole always resolves to true as the return loadEntities() part does not pass me back the value of my enclosed return inside but the promise itself. And without it, it always resolves to false(I think its an undefined).
So in short. The first one works. How do I get my hands on the value of duplicateFound outside this method?
the function as a whole always resolves to true
No, it does not return true. It does return a promise for a boolean value, which might fulfill with true (or with false or reject altogether). That promise of course is a truthy value, so you cannot use it right inside of an if condition.
Instead, you have to adapt the calling code to anticipate the promise, and let it wait for the resolution before inspecting the value. Of course, this makes your whole function (and everything that calls it) asynchronous.
return isNameTaken().then(function(taken) {
if (taken)
…
else
…
}); // yields another promise for the result of the callback

Categories

Resources