Use asynchronous function inside _.map() - javascript

I am using lodash library for iteration. I have used _.map() function for this purpose. I have a problem that I have an asynchronous function inside _.map(). My code is like this:
let dataToRender = {};
let modifiedData = _.map(myArray, (element) => {
dataToRender.uuid = element.UID;
dataToRender.pendingAmountReceived = element.$.SR;
dataToRender.orderID = element.TransactionID;
dataToRender.orderAmount = element.TransactionValue;
dataToRender.orderDate = moment(element.TransactionTime, 'YYYY-MM-DDTHH:mm:ss');
dataToRender.goal = element.Product.replace('amp;', '&');
dataToRender.currentStatus = 'waiting';
//Here CheckFilter is my asynchrounous function
//I have to do calculation base on the response of this checkFilter
checkFilter(requireParams)
.then(data => {
//If the response returned from checkFilter is false I have to include this data
if (!data) {
return requireParams;
}
});
});
res.json(modifiedData);
But I am getting just null in modifiedData. How to get all the data?

Bluebird provides a number of promise helper methods including Promise.map() which will only resolve when all returned promises are resolved.
The only problem is you will end up with undefined values in your results for when the checkFilter function is truthey. lodash's _.compact function can help here or you can use _.filter if you have more specific filtering needs.
const Promise = require('bluebird');
let dataToRender = {};
Promise.map(myArray, element => {
dataToRender.uuid = element.UID;
dataToRender.pendingAmountReceived = element.$.SR;
dataToRender.orderID = element.TransactionID;
dataToRender.orderAmount = element.TransactionValue;
dataToRender.orderDate = moment(element.TransactionTime, 'YYYY-MM-DDTHH:mm:ss');
dataToRender.goal = element.Product.replace('amp;', '&');
dataToRender.currentStatus = 'waiting';
return checkFilter(requireParams)
.then(data => {
// If the response returned from checkFilter is false I have to include this data
if (!data) return requireParams;
});
})
.then(results => res.json(_.compact(results)) );

Related

Promise or Async

Hopefully someone can help me correct the below code.
I am not able to get anything in the final return statement.
I know it looks messy as I have tried a few alternatives.
async function concatnames(allSelectedRows){
var flightName = [];
var name = "";
var resultOutCome = "";
try {
var result = await allSelectedRows.forEach(
function (selectedRow, i){
recordidGuid = selectedRow.getData().getEntity().getEntityReference().id.replace('{', '').replace('}', '') //Flight Plan Event Guid
name = Xrm.WebApi.online.retrieveRecord("crd80_flightplanevent", recordidGuid, "?$select=_crd80_learnerflight_value")
})
resultOutCome = name["_crd80_learnerflight_value#OData.Community.Display.V1.FormattedValue"]
console.log(resultOutCome)
flightName.push(resultOutCome);
}
catch (error) {
DisplayError(error)
}
return flightName
}
await only works with promises, allSelectedRows.forEach(...) it's not a promise, it's a function.
You want to do something like this:
await Promise.all(allSelectedRows.forEach(
async function (selectedRow, i) {
recordidGuid = await selectedRow.getData().getEntity().getEntityReference().id.replace('{', '').replace('}', '') //Flight Plan Event Guid
name = await Xrm.WebApi.online.retrieveRecord("crd80_flightplanevent", recordidGuid, "?$select=_crd80_learnerflight_value")
}))
The Promise.all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises. This returned promise will resolve when all of the input's promises have resolved, or if the input iterable contains no promises.
And the new allSelectedRows.forEach(...) with now async functions, returns promises, because when you do async function NameOfFunction, you are actually creating a promise, that Promise.all() will return all them resolved.
And I am using an await in Xrm.WebApi.online.retrieveRecord("crd80_flightplanevent", recordidGuid, "?$select=_crd80_learnerflight_value") because I think it is a promise, so when I add await, it will return me the resolved value of the promise.
Depending on what you to accomplish, there's a couple of ways to do this:
Process Each Request Sequentially
This is (a) slower than the alternative, and (b) more code. However, it gives you granular control of the error(s) encountered:
// process each request sequentially
async function concatnames( allSelectedRows ) {
const flightName = [];
let name = "";
let resultOutCome = "";
try {
for ( const selectedRow of allSelectedRows ) {
recordidGuid = selectedRow
.getData()
.getEntity()
.getEntityReference()
.id
.replace('{', '')
.replace('}', '');
name = await Xrm
.WebApi
.online
.retrieveRecord(
"crd80_flightplanevent",
recordidGuid,
"?$select=_crd80_learnerflight_value"
);
resultOutCome = name["_crd80_learnerflight_value#OData.Community.Display.V1.FormattedValue"];
flightName.push(resultOutCome);
}
}
catch (error) {
DisplayError(error)
}
return flightName
}
Run the requests in parallel
This is nice and clean. Also faster than the above because all request are run in parallel:
async function concatnames(allSelectedRows) {
const flightName = await Promise.all( allSelectedRows.map( row => {
recordidGuid = selectedRow
.getData()
.getEntity()
.getEntityReference()
.id
.replace('{', '')
.replace('}', '');
return Xrm.WebApi.online
.retrieveRecord(
"crd80_flightplanevent",
recordidGuid,
"?$select=_crd80_learnerflight_value"
)
.then( name => name["_crd80_learnerflight_value#OData.Community.Display.V1.FormattedValue"]);
}))
.catch( DisplayError );
return flightName || [];
}

How to change variable value in Axios response?

I'm trying to change a variable value in axios's then. Here is my example:
var pts = [];
axios.get('http://www.example.com')
.then(function (response) {
pts = response.data;
});
console.log(pts);
The pts variable just returns empty array. I just want to use it out of the GET query. How can I change it?
use the new elegant way async & await it is more neat and readable
let pts = [];
const getData = async () => {
const {data} = await axios.get("http://www.example.com");
pts = data;
return data
}
getData();
console.log(pts);
The request is asynchronous so the pts gets printed first and then the function gets called
What you can do is use a promise and make your request as follows:
const func1 = () => {
return axios.get("http://www.example.com").then(response => {return response})
}
func1().then(data => {
pts = data;
})

Why am I not being able to use position 0 of an array with __proto__?

I am not able to use an array that I have in the code. I have no idea why __proto__ is there
It looks like a normal array, but when I want to access the values of position 0 (arr [0]), the console returns "undefined".
here is the javascript code:
const apiUrl = 'https://api.covid19api.com/dayone/country/south-africa';
async function getData(){
const response = await fetch(apiUrl);
const data = await response.json();
const arr = data.reduce((acc, cur) => cur.Confirmed ? [...acc, cur.Confirmed] : acc, []);
return arr;
}
var res = [];
getData().then( val => {
res.push(val);
})
console.log(res);
It is because the line console.log(res); is finished faster than the function getData().
const apiUrl = 'https://api.covid19api.com/dayone/country/south-africa';
async function getData(){
const response = await fetch(apiUrl);
const data = await response.json();
const arr = data.reduce((acc, cur) => cur.Confirmed ? [...acc, cur.Confirmed] : acc, []);
return arr;
}
var res = [];
getData().then( val => {
res.push(val);
console.log(res);
console.log(res[0]);
})
Put the statement inside the function getDate() or use .then() again to handle the data afterward.
const apiUrl = 'https://api.covid19api.com/dayone/country/south-africa';
async function getData(){
const response = await fetch(apiUrl);
const data = await response.json();
const arr = data.reduce((acc, cur) => cur.Confirmed ? [...acc, cur.Confirmed] : acc, []);
return arr;
}
var res = [];
function showData(callback){
getData().then( val => {
res.push(val);
callback();
});
}
showData( function(){
console.log(res[0])
});
Here's another solution, almost the same as TmasYIN's. you can add a callback function that's executed inside .then() function, and that callback function is defined when you execute showData.
Try this:
const apiUrl = 'https://api.covid19api.com/dayone/country/south-africa';
async function getData(){
const response = await fetch(apiUrl);
const data = await response.json();
const arr = data.reduce((acc, cur) => cur.Confirmed ? [...acc, cur.Confirmed] : acc, []);
return arr;
}
function showData(callback){
getData().then( val => {
res.push(val);
});
setTimeout(function() {
callback();
}, 1500);
}
showData( function(){
console.log(res[0])
});
proto is a built-in javascript object's inheritance pointer that points to its parent. (In javascript, function is also an object). In your example, getData() is inherited from AsyncFunction, so if you call getData().proto, it gives you an object of AsyncFunction. You can get the result after you give it some time to fetch data.
You can't access the result of a promise (i.e. its fulfilled value) immediately after registering a handler on a promise, because promise code never calls handlers provided to it synchronously.
When a promise becomes resolved or rejected, promise code puts jobs to execute handlers (and monitor their behavior) in a promise job queue, leaving executing tasks from the queue to the event loop manager.
This demonstration shows data is not transferred by then in the same call out from the event loop, but can be available after returning to the event loop:
let dataPromise = new Promise( resolve => resolve("hello, I'm data"));
var res=[];
res.ready = false;
dataPromise.then(data => {
res.push(data);
res.ready = true;
});
function processData() {
console.log(
"ready: %s", data: %s",
res.ready, res[0]);
}
processData(); // won't work
setTimeout( processData, 20); // will work
Data obtained using a promise is only available through the Promise interface. Code using the data can register a call back on the data promise by calling the then method of the promise, or using await before the promise inside an async function.
Obviously in a practical application, data also won't be available from a server request until after the server has responded, the browser received the reply and notified script code of the outcome.

Using promise in loop results in Promise failure

I'd like to reuse the same code in a loop. This code contains promises. However, when iterating, this code results in an error.
I've tried using for and while loops. There seems to be no issue when I use the for loop for a single iteration.
Here is a minimal version of my code:
var search_url = /* Some initial URL */
var glued = "";
for(var i = 0; i < 2; i++)
{
const prom = request(search_url)
.then(function success(response /* An array from a XMLHTTPRequest*/) {
if (/* Some condition */)
{
search_url = /* Gets next URL */
glued += processQuery(response[0]);
} else {
console.log("Done.")
}
})
.catch(function failure(err) {
console.error(err.message); // TODO: do something w error
})
}
document.getElementById('api-content').textContent = glued;
I expect the results to append to the variable glued but instead, I get an error: failure Promise.catch (async) (anonymous) after the first iteration of the loop.
Answer:
You can use the Symbol.iterator in accordance with for await to perform asynchronous execution of your promises. This can be packaged up into a constructor, in the example case it's called Serial (because we're going through promises one by one, in order)
function Serial(promises = []) {
return {
promises,
resolved: [],
addPromise: function(fn) {
promises.push(fn);
},
resolve: async function(cb = i => i, err = (e) => console.log("trace: Serial.resolve " + e)) {
try {
for await (let p of this[Symbol.iterator]()) {}
return this.resolved.map(cb);
} catch (e) {
err(e);
}
},
[Symbol.iterator]: async function*() {
this.resolved = [];
for (let promise of this.promises) {
let p = await promise().catch(e => console.log("trace: Serial[Symbol.iterator] ::" + e));
this.resolved.push(p);
yield p;
}
}
}
}
What is the above?
It's a constructor called Serial.
It takes as an argument an array of Functions that return Promises.
The functions are stored in Serial.promises
It has an empty array stored in Serial.resolved - this will store the resolved promise requests.
It has two methods:
addPromise: Takes a Function that returns a Promise and adds it to Serial.promises
resolve: Asynchronously calls a custom Symbol.iterator. This iterator goes through every single promise, waits for it to be completed, and adds it to Serial.resolved. Once this is completed, it returns a map function that acts on the populated Serial.resolved array. This allows you to simply call resolve and then provide a callback of what to do with the array of responses. A.e. .resolve()((resolved_requests) => //do something with resolved_requests)
Why does it work?
Although many people don't realize this Symbol.iterator is much more powerful than standard for loops. This is for two big reasons.
The first reason, and the one that is applicable in this situation, is because it allows for asynchronous calls that can affect the state of the applied object.
The second reason is that it can be used to provide two different types of data from the same object. A.e. You may have an array that you would like to read the contents of:
let arr = [1,2,3,4];
You can use a for loop or forEach to get the data:
arr.forEach(v => console.log(v));
// 1, 2, 3, 4
But if you adjust the iterator:
arr[Symbol.iterator] = function* () {
yield* this.map(v => v+1);
};
You get this:
arr.forEach(v => console.log(v));
// 1, 2, 3, 4
for(let v of arr) console.log(v);
// 2, 3, 4, 5
This is useful for many different reasons, including timestamping requests/mapping references, etc. If you'd like to know more please take a look at the ECMAScript Documentation: For in and For Of Statements
Use:
It can be used by calling the constructor with an Array of functions that return Promises. You can also add Function Promises to the Object by using
new Serial([])
.addPromise(() => fetch(url))
It doesn't run the Function Promises until you use the .resolve method.
This means that you can add promises ad hoc if you'd like before you do anything with the asynchronous calls. A.e. These two are the same:
With addPromise:
let promises = new Serial([() => fetch(url), () => fetch(url2), () => fetch(url3)]);
promises.addPromise(() => fetch(url4));
promises.resolve().then((responses) => responses)
Without addPromise:
let promises = new Serial([() => fetch(url), () => fetch(url2), () => fetch(url3), () => fetch(url4)])
.resolve().then((responses) => responses)
Data:
Since I can't really replicate your data calls, I opted for JSONPlaceholder (a fake online rest api) to show the promise requests in action.
The data looks like this:
let searchURLs = ["https://jsonplaceholder.typicode.com/todos/1",
"https://jsonplaceholder.typicode.com/todos/2",
"https://jsonplaceholder.typicode.com/todos/3"]
//since our constructor takes functions that return promises, I map over the URLS:
.map(url => () => fetch(url));
To get the responses we can call the above data using our constructor:
let promises = new Serial(searchURLS)
.resolve()
.then((resolved_array) => console.log(resolved_array));
Our resolved_array gives us an array of XHR Response Objects. You can see that here:
function Serial(promises = []) {
return {
promises,
resolved: [],
addPromise: function(fn) {
promises.push(fn);
},
resolve: async function(cb = i => i, err = (e) => console.log("trace: Serial.resolve " + e)) {
try {
for await (let p of this[Symbol.iterator]()) {}
return this.resolved.map(cb);
} catch (e) {
err(e);
}
},
[Symbol.iterator]: async function*() {
this.resolved = [];
for (let promise of this.promises) {
let p = await promise().catch(e => console.log("trace: Serial[Symbol.iterator] ::" + e));
this.resolved.push(p);
yield p;
}
}
}
}
let searchURLs = ["https://jsonplaceholder.typicode.com/todos/1", "https://jsonplaceholder.typicode.com/todos/2", "https://jsonplaceholder.typicode.com/todos/3"].map(url => () => fetch(url));
let promises = new Serial(searchURLs).resolve().then((resolved_array) => console.log(resolved_array));
Getting Results to Screen:
I opted to use a closure function to simply add text to an output HTMLElement.
This is added like this:
HTML:
<output></output>
JS:
let output = ((selector) => (text) => document.querySelector(selector).textContent += text)("output");
Putting it together:
If we use the output snippet along with our Serial object the final functional code looks like this:
let promises = new Serial(searchURLs).resolve()
.then((resolved) => resolved.map(response =>
response.json()
.then(obj => output(obj.title))));
What's happening above is this:
we input all our functions that return promises. new Serial(searchURLS)
we tell it to resolve all the requests .resolve()
after it resolves all the requests, we tell it to take the requests and map the array .then(resolved => resolved.map
the responses we turn to objects by using .json method. This is necessary for JSON, but may not be necessary for you
after this is done, we use .then(obj => to tell it to do something with each computed response
we output the title to the screen using output(obj.title)
Result:
let output = ((selector) => (text) => document.querySelector(selector).textContent += text)("output");
function Serial(promises = []) {
return {
promises,
resolved: [],
addPromise: function(fn) {
promises.push(fn);
},
resolve: async function(cb = i => i, err = (e) => console.log("trace: Serial.resolve " + e)) {
try {
for await (let p of this[Symbol.iterator]()) {}
return this.resolved.map(cb);
} catch (e) {
err(e);
}
},
[Symbol.iterator]: async function*() {
this.resolved = [];
for (let promise of this.promises) {
let p = await promise().catch(e => console.log("trace: Serial[Symbol.iterator] ::" + e));
this.resolved.push(p);
yield p;
}
}
}
}
let searchURLs = ["https://jsonplaceholder.typicode.com/todos/1", "https://jsonplaceholder.typicode.com/todos/2", "https://jsonplaceholder.typicode.com/todos/3"].map(url => () => fetch(url));
let promises = new Serial(searchURLs).resolve()
.then((resolved) => resolved.map(response =>
response.json()
.then(obj => output(obj.title))));
<output></output>
Why go this route?
It's reusable, functional, and if you import the Serial Constructor you can keep your code slim and comprehensible. If this is a cornerstone of your code, it'll be easy to maintain and use.
Using it with your code:
I will add how to specifically use this with your code to fully answer your question and so that you may understand further.
NOTE glued will be populated with the requested data, but it's unnecessary. I left it in because you may have wanted it stored for a reason outside the scope of your question and I don't want to make assumptions.
//setup urls:
var search_urls = ["https://jsonplaceholder.typicode.com/todos/1", "https://jsonplaceholder.typicode.com/todos/2"];
var request = (url) => () => fetch(url);
let my_requests = new Serial(search_urls.map(request));
//setup glued (you don't really need to, but if for some reason you want the info stored...
var glued = "";
//setup helper function to grab title(this is necessary for my specific data)
var addTitle = (req) => req.json().then(obj => (glued += obj.title, document.getElementById('api-content').textContent = glued));
// put it all together:
my_requests.resolve().then(requests => requests.map(addTitle));
Using it with your code - Working Example:
function Serial(promises = []) {
return {
promises,
resolved: [],
addPromise: function(fn) {
promises.push(fn);
},
resolve: async function(cb = i => i, err = (e) => console.log("trace: Serial.resolve " + e)) {
try {
for await (let p of this[Symbol.iterator]()) {}
return this.resolved.map(cb);
} catch (e) {
err(e);
}
},
[Symbol.iterator]: async function*() {
this.resolved = [];
for (let promise of this.promises) {
let p = await promise().catch(e => console.log("trace: Serial[Symbol.iterator] ::" + e));
this.resolved.push(p);
yield p;
}
}
}
}
//setup urls:
var search_urls = ["https://jsonplaceholder.typicode.com/todos/1", "https://jsonplaceholder.typicode.com/todos/2"];
var request = (url) => () => fetch(url);
let my_requests = new Serial(search_urls.map(request));
//setup glued (you don't really need to, but if for some reason you want the info stored...
var glued = "";
//setup helper function to grab title(this is necessary for my specific data)
var addTitle = (req) => req.json().then(obj => (glued += obj.title, document.getElementById('api-content').textContent = glued));
// put it all together:
my_requests.resolve().then(requests => requests.map(addTitle));
<div id="api-content"></div>
Final Note
It's likely that we will be seeing a prototypal change to the Promise object in the future that allows for easy serialization of Promises. Currently (7/15/19) there is a TC39 Proposal that does add a lot of functionality to the Promise object but it hasn't been fully vetted yet, and as with many ideas trapped within the Proposal stage, it's almost impossible to tell when they will be implemented into Browsers, or even if the idea will stagnate and fall off the radar.
Until then workarounds like this are necessary and useful( the reason why I even went through the motions of constructing this Serializer object was for a transpiler I wrote in Node, but it's been very helpful beyond that! ) but do keep an eye out for any changes because you never know!
Hope this helps! Happy Coding!
Your best bet is probably going to be building up that glued variable with recursion.
Here's an example using recursion with a callback function:
var glued = "";
requestRecursively(/* Some initial URL string */, function() {
document.getElementById('api-content').textContent = glued;
});
function requestRecursively(url, cb) {
request(url).then(function (response) {
if (/* Some condition */) {
glued += processQuery(response[0]);
var next = /* Gets next URL string */;
if (next) {
// There's another URL. Make another request.
requestRecursively(next, cb);
} else {
// We're done. Invoke the callback;
cb();
}
} else {
console.log("Done.");
}
}).catch(function (err) {
console.error(err.message);
});
}

JavaScript promise bypassed resolve and proceed to .then()

I was having some problem with nested promise which resulting in forgotten promise problem.
let promiseList = new Promise((resolve, reject) => {
//first query to retrieve from firebase
query.once( 'value', data => {
var promises = [];
data.forEach(snapshot => {
//get item key
//second query based on item key
var promise = query.once('value');
promises.push(promise);
promise.then(data => {
var itemDetail = data.val();
var receiptID = itemDetail.receiptID;
// third query to find matching receiptID
var query = firebase.database().ref('receipts');
query.once('value', data => {
data.forEach(snapshot => {
snapshot.forEach(childSnapshot => {
if(childSnapshot.key == receiptID){
var branchDetail = childSnapshot.val().branch;
var branchName = branchDetail.branchName;
//console.log('inside promise ' + branchName);
datasetarr.push({branchName: branchName});
}
});
});
});
});
});
// wait till all promises are finished then resolve the result array
Promise.all(promises).then(() => resolve(datasetarr));
});
});
// print out array here
promiseList.then((arr) => {
for(var i = 0; i < arr.length; i++){
console.log(arr[i].branchName);
}
});
I managed to print out the data from the console.log with 'inside promise'. However, when I tried to print it out from the .then(), there is nothing shown.
The problem now is it actually ran the .then() first before I resolve the promise.
Any ideas?
I've never used Firebase, but I do know promises.
Check this sample chaining promises, notice the return statements which produce the chaining.
var outerPromise = query.once('value').then(data => {
// Promise array to group 2nd level promises and then do a Promise.all.
var promises = [];
// This will be the main output of the outerPromise.
// We will populate it asynchronously inside our 2nd level promises.
var datasetarr = [];
data.forEach(snapshot => {
// 2nd level promises, will be appended to the promises array.
// and will be enchained with the 3d level promise.
var promise = query.once('value').then(data => {
var itemDetail = data.val();
var receiptID = itemDetail.receiptID;
var query = firebase.database().ref('receipts');
// Third level promise. It's enchained by the return statement.
return query.once('value').then(data => {
data.forEach(snapshot => {
snapshot.forEach(childSnapshot => {
if(childSnapshot.key == receiptID){
var branchDetail = childSnapshot.val().branch;
var branchName = branchDetail.branchName;
//console.log('inside promise ' + branchName);
datasetarr.push({branchName: branchName});
}
});
});
});
});
promises.push(promise);
});
// We wait until 2nd (and third) level promises are ready
// and the return our desired output, the datasetarr
return Promise.all(promises).then(()=> datasetarr);
});
// Since it's all chained, the outerPromise will resolve once all promises are completed
// and we can get the output we supplied in the last chaining.
outerPromise.then((arr) => {
console.log(arr)
});
That's not how promises work, there's seldom if ever a need to nest them. If query.once already returns a promise that's great, but otherwise you'll need to wrap it:
let returnsPromise = value => new Promise(res => query.once(value, data => res(data));
Again, if it already returns a promise that's unnecessary, but I'm not a firebase guy. At any rate, now you can do something like this:
let result = returnsPromise('value')
// run secondary query based on item key
.then(data => Promise.all(data.map(item => returnsPromise(item.key)))
// now do stuff with those results
.then(data => {
return Promise.all(data.map(item => {
let receiptID = item.val().receiptID;
// Note that the same 'wrap if not already returning promise
// idea' is applicable here, but for illustration I'm just
// going to act like this returns a promise.
// Also note that while I've been rather down on nesting
// its more or less necessary here because you need to capture
// the receipt ID from the surrounding scope.
return firebase.database().ref('receipts')
.once('value')
.then(snapshot => {
return snapshot
.filter(x => x.key === receiptID)
.map(x => {
let branch = x.val().branch.branchName;
return {branch: branch};
});
});
}))
// Now we have an array of arrays of results but we want to
// remove the nesting.
.then(arrayOfArrays => arrayOfArrays.reduce((x,y) => { return x.concat(y); }, []));
Now you have a result promise that contains the array of values. You can call then on it and iterate over it:
result.then(arr => arr.forEach(x => console.log(x.branchName)));

Categories