Javascript reduce in other function - javascript

How can I get the data I parse from my JSON file to run through the reduce function to eliminate duplicates and then beeing available by calling the getFiilteredData() function?
async function getFilteredData() {
return new Promise((resolve) => {
oWebViewInterface.on("loadData", function (data) {
var schwellWerte = data.monitor;
var monitorData = data.data.reduce((arr, d) => {
if (arr.find((i) => i.zeitstempel === d.zeitstempel)) {
return arr;
} else {
return [...arr, d];
}
}, []);
resolve(monitorData); // resolve the promise with the data
//can I do: resolve(monitorData, schwellWerte) to resolve both?
});
});
}
Doing it like this, results in "Uncaught TypeError: Cannot read property '0' of undefined" for the two last console.log() but the first works fine and logs the expected value.

The easiest way is to use a Promise and async/await. Wrap your asynchronous call in a Promise and await it at the client:
async function getFilteredData() {
return new Promise( resolve => {
oWebViewInterface.on("loadData", function (data) {
var monitorData = JSON.parse(data).reduce((arr, d) => {
if (arr.find((i) => i.zeitstempel === d.zeitstempel)) {
return arr;
} else {
return [...arr, d];
}
}, []);
resolve(monitorData); // resolve the promise with the data
});
});
}
and then when you call it just await the call
var filteredData = await getFilteredData();
console.log(filteredData[0].id);
Edit: I notice from your comments that in your code you're calling getFilteredData twice - this seems like a bad idea. Call it once. If you put the configuration of your chart into its own async method this gets easier
async function configChart(){
var data = await getFilteredData();
var werteArr = [];
var zsArr = [];
for (i = 0; i < data.length; i++) {
werteArr.push(data[i].wert);
zsArr.push(data[i].zeitstempel);
}
//defining config for chart.js
var config = {
type: "line",
data: {
labels: zsArr ,
datasets: {
data: werteArr,
// backgroundcolor: rgba(182,192,15,1),
},
},
// -- snip rest of config -- //
}
var ctx = document.getElementById("canvas").getContext("2d");
window.line_chart = new window.Chart(ctx, config);
}
window.onload = function () {
configChart(); // no need to await this. It'll happen asynchronously
};

Related

Use async iterator triggered by a custom function in main scope

What I want to do is to create an iterator, which is only triggered when an external function is called, say an external event.
An iterator that simply waits for custom events.
function createIteratorWithFunction() {
var thingThatResolves;
var asyncIterable = {
thingThatResolves,
[Symbol.asyncIterator]() {
return {
next() {
return (new Promise((resolve, reject) => asyncIterable.thingThatResolves = (resolve))).then(_ => ({
value: _,
done: false
}));
},
return () {
return {
done: true
}
}
};
}
};
return asyncIterable;
}
iter = createIteratorWithFunction();
(async function() {
for await (let val of iter) {
console.log(val);
}
})()
<button onclick="iter.thingThatResolves('execute');iter.thingThatResolves(3)">execute next!</button>
As you can see, it only resolves 'execute', but not 3, of course because promises can't be resolved more than once, and it only is updated asynchronously, I understand this, but since the iterator is async, how would I create a queue, so that any values that could've synchronously been triggered are retrieved by next(), as well?
I have this feeling that there's a more elegant solution involving promise chains, but it's escaping me at the moment. :-) See inline comments:
function createIteratorWithFunction() {
// Our pending promise, if any
let promise = null;
// The `resolve` function for our `pending` promise
let resolve = null;
// The values in the queue
const values = [];
// The async iterable
const asyncIterable = {
add(value) {
// Add a value to the queue; if there's a pending promise, fulfill it
values.push(value);
const r = resolve;
resolve = pending = null;
r?.();
},
[Symbol.asyncIterator]() {
return {
async next() {
// If we don't have a value...
while (!values.length) {
// ...we need to wait for one; make sure we have something
// to wait for
if (!resolve) {
pending = new Promise(r => { resolve = r; });
}
await pending;
}
// Get the value we waited for and return it
const value = values.shift();
return {
value,
done: false,
};
},
return() {
return {
done: true,
};
}
};
}
};
return asyncIterable;
}
const iter = createIteratorWithFunction();
(async function() {
for await (let val of iter) {
console.log(val);
}
})();
document.getElementById("execute").addEventListener("click", () => {
iter.add("execute");
iter.add(3);
});
<button id="execute">execute next!</button>
One of the key things here is that an async iterable can have overlapping iterations, and it has to not get confused by that. This implementation avoids that by creating the promise it'll wait on synchronously if it needs one.
function createIteratorWithFunction() {
// Our pending promise, if any
let promise = null;
// The `resolve` function for our `pending` promise
let resolve = null;
// The values in the queue
const values = [];
// The async iterable
const asyncIterable = {
add(value) {
// Add a value to the queue; if there's a pending promise, fulfill it
values.push(value);
const r = resolve;
resolve = pending = null;
r?.();
},
[Symbol.asyncIterator]() {
return {
async next() {
// If we don't have a value...
while (!values.length) {
// ...we need to wait for one; make sure we have something
// to wait for
if (!resolve) {
pending = new Promise(r => { resolve = r; });
}
await pending;
}
// Get the value we waited for and return it
const value = values.shift();
return {
value,
done: false,
};
},
return() {
return {
done: true,
};
}
};
}
};
return asyncIterable;
}
const iter = createIteratorWithFunction();
(async function() {
for await (let val of iter) {
console.log("first:", val);
}
})();
(async function() {
for await (let val of iter) {
console.log("second:", val);
}
})();
document.getElementById("execute").addEventListener("click", () => {
iter.add("execute");
iter.add(3);
});
<button id="execute">execute next!</button>
I'm never happy when I have to make the promise's resolve function accessible outside the promise executor function (the function you pass new Promise), but as I say, the elegant solution with promise chains is escaping me. I sense strongly that it's there...somewhere... :-)
Another idea & way of doing this, you could use custom events, one advantage is that the code is much easier to reason with.
Below I've knocked up a simple example, it also allows you to cancel the iterator & handle errors. makeIter simple gives you 4 functions,
add = use this to add an item to the itterator.
iter = this is the iterator you can for await on.
done = if were done, you can call this and let the GC do it's thing.
error = allows you to put an error into the iterator, you can test this by un-commenting the last line.
To prevent any race conditions I've simply used a stack..
function makeIter() {
const obj = new EventTarget();
const evName = 'my-iter';
const stack = [];
obj.addEventListener(evName, e => {
stack.push(e.detail);
resolve();
});
async function *iter() {
while (true) {
await new Promise(r => resolve = r);
while (stack.length) {
const s = stack.shift();
if (s.resolve) yield(s.resolve);
if (s.reject) throw s.reject;
if (s.cancel) return;
}
}
}
function ev(p) {
obj.dispatchEvent(new CustomEvent(evName, {detail:p}));
}
return {
error: (e) => ev({reject: e}),
done: () => ev({cancel: true}),
add: item => ev({resolve: item}),
iter: iter()
}
}
///testing...
const test = makeIter();
(async function () {
try {
for await (const item of test.iter) {
console.log(item);
}
} finally {
console.log('iter done');
}
}());
test.add('hello');
setTimeout(() => test.add('there'), 100);
setTimeout(() => {test.add('1'); test.add('2'); test.add('3'); }, 200);
setTimeout(() => test.add('4'), 400);
setTimeout(() => test.done(), 1000);
//setTimeout(() => test.error(new Error('oops')), 1000);

How to make async/wait inside of for loop?

I'm using async await inside of for loop as below.
for (let i = 0; i < result.length; i += 1) {
try{
const client = await axios.get(
`${process.env.user}/client/${result[i].id}`
);
} catch(error){
console.log(error)
}
if (client.data.success === true) {
result[i].Name = rider.data.client.Name;
result[i].PhoneNumber = rider.data.client.Number;
}
}
But I want to make this using 'new Promise' and 'promiss.all' to make it asynclously.
But I don'k know how to make this correctly doing error handle well.
Could you recommend some advice for this? Thank you for reading it.
This can be a basic solution, i think
let playList = []
for (let i = 0; i < result.length; i += 1) {
playList.push(axios.get(
`${process.env.user}/client/${result[i].id}`
).then(res => {
if (res.data.success === true) {
result[i].Name = rider.data.client.Name;
result[i].PhoneNumber = rider.data.client.Number;
}
}).catch(ex => console.log(ex)));
}
await Promise.all(playList)
This can also be done by using a foreach loop.
The for/foreach loop can be simplified by using a map function.
Js map function is equivalent of c# select linq function.
The fat arrow in js map function is not bound to return a value unlike c# select inner function which must return a value.
await Promise.all(result.map(async r => {
let client;
try {
client = await axios.get(`${process.env.user}/client/${r.id}`);
} catch (error) {
console.log(error)
}
if (client.data.success === true) {
r.Name = rider.data.client.Name;
r.PhoneNumber = rider.data.client.Number;
}
}));
Try this
var promises = result.map(r => axios.get(`${process.env.user}/client/${r.id}`);
Promise.all(promises).then(function(values) {
console.log('All promises done');
});
The idea is that if you are awaiting something, that is promise, you can await it, or call it to get promise
Example:
function Foo()
{
return new Promise(...); // Promise of int for example
}
you can do
var p = Foo(); //you will get promise
Or
var v = await Foo(); // you will get int value when promise resolved
This is how you do it with async/await + Promise.all:
const myResult = await Promise.all(result.map(({ id }) => {
return axios.get(`${process.env.user}/client/${id}`);
}));
// deal with the result of those requests
const parsed = myResult.map(data => /* your code here */);
Here is an example using Array.map to call your function along with Promise.all. I wrapped the axios request in a function so if one of your request fails, it wont stop every other requests. If you don't mind stopping when you got an issue, look at others answers to your question.
function fakeRequest() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
data: {
success: true,
client: {
Name: 'Todd',
Number: 5,
},
},
});
}, 300);
});
}
(async() => {
const result = [{}, {}, {}];
await Promise.all(result.map(async(x, xi) => {
try {
const client = await fakeRequest();
if (client.data.success === true) {
result[xi].Name = client.data.client.Name;
result[xi].PhoneNumber = client.data.client.Number;
}
} catch (err) {
console.log(err)
}
}));
console.log(result);
})();

JavaScript: Returning array from recursive function

I have made a class which builds some data from api:
const http = require("http");
class VideoService {
constructor() {
this.items = [];
}
fetchVideos(token = "") {
const url = `https://www.example.com`;
http.getJSON(url).then((results) => {
results.items.forEach((item, index) => {
const vid = item.snippet.resourceId.videoId;
this.items.push({
title: item.title,
date: item.publishedAt
});
console.log(this.items.length); // here length inreases, works here
});
if (typeof results.nextPageToken !== "undefined") {
return this.fetchVideos(results.nextPageToken);
}
});
}
getVideos() {
this.fetchVideos();
console.log(this.items.length); // this returns 0 instead of all items fetched
return this.items;
}
}
module.exports = VideoService;
In another file, I am using it like this:
const videoService = require("../shared/videoService");
const videos = (new videoService()).getVideos();
console.log(videos);
The last console.log call always returns empty array instead of all data collected in items property of the above class.
Can anybody tell what I am missing here?
This happens because in your function fetchVideos(), you are making an http call which will be processed asynchronously. You can try to process it this way.
fetchVideos(token = "") {
const url = `https://www.example.com`;
return http.getJSON(url).then((results) => {
results.items.forEach((item, index) => {
const vid = item.snippet.resourceId.videoId;
this.items.push({
title: item.title,
date: item.publishedAt
});
console.log(this.items.length); // here length inreases, works here
});
if (typeof results.nextPageToken !== "undefined") {
return this.fetchVideos(results.nextPageToken);
}
else return new Promise((resolve, reject)=>{
resolve();
});
});
}
getVideos() {
return this.fetchVideos().then(function(){
console.log(this.items.length); // this returns 0 instead of all items fetched
return this.items;
});
}
I suggest reading about promises and asynchronicity in javascript. Check this link:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Using Promise.all

Firstly apologies if this is a simple problem to solve but I am using node-fetch and the concept of Promises for the first time. I am calling an api endpoint which returns paginated data so i get multiple links for further data.
I am handling this by putting all the links into an array and then I want to call each endpoint in the array and just print out the data to the console for now
So my first call is
var urlArray = [];
fetch(`${BASE_URL}/leagues?api_token=${AUTH_TOKEN}`, { headers: headers })
.then(function(response){
return response.json(); // pass the data as promise to next then block
})
.then(function(json){
// Collect Paginated Results
var number_of_pages = parseInt(json.meta['pagination']['total_pages']);
for (i = 2; i < number_of_pages + 1; i++) {
urlArray.push(`${BASE_URL}?page=${i}&api_token=${AUTH_TOKEN}`)
}
})
.catch(function(error) {
console.log(error);
});
So once this done i have an array of urls i need to hit sequentially, could anyone give me some pointers please on how to then setup a promise to hit each one? or can i have something where is do it in my for loop?
Thanks
sorry if this is wrong!
At the beginning, put:
var promise_array = [];
in the for loop, replace with
for (i = 2; i < number_of_pages + 1; i++) {
var url = `${BASE_URL}?page=${i}&api_token=${AUTH_TOKEN}`;
urlArray.push(url);
promise_array.push(fetch(url));
}
At the end of the function put
Promise.all(promise_array).then(all_responses => {
// .. pluck json, then continue ...
});
You can achieve this by:
fetch(`${BASE_URL}/leagues?api_token=${AUTH_TOKEN}`, { headers: headers }).then(response => {
var json = response.json();
var numberOfPages = parseInt(json.meta['pagination']['total_pages']);
var urlArray = [];
for (i = 2; i < numberOfPages + 1; i++) {
urlArray.push(`${BASE_URL}?page=${i}&api_token=${AUTH_TOKEN}`)
}
var promises = urlArray.map(url => getSomethingByUrl(url));
return Promise.all(promises);
}).then(results => {
console.log(results);
});
You can use Array.prototype.reduce to create Promise chain so they will be executed one after another
urls.reduce((prev, item) => {
return prev.then(() => {
return someAction(item);
})
}, Promise.resolve()).then(() => {
console.log('All done');
}).catch((err) => {
console.log(err);
});
Edit:
As mentioned by Bergi, value from someAction should be returned in case it's asynchronous (return Promise). If someAction is asynchronous it must return Promise.
"use strict";
function someAction(i) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(i);
resolve(i);
}, i*1000);
});
}
const urls = [1,2,3,4,5];
urls.reduce((prev, item) => {
return prev.then(() => {
return someAction(item);
})
}, Promise.resolve()).then(() => {
console.log('All done');
}).catch((err) => {
console.log(err);
});

"A promise was created in a handler but was not returned from it"

When a user clicks on a button (#lfdsubmit), it calls the function (LFD_SearchContainer()) that should return a promise. But the errors happens at
LFD_SearchContainer('EISU1870725')
.then(container => {
ST2.db2(container);
})
What is wrong?
Code: (don't completely trust the commented out parts to guide you through this code -- i forgot to update some of them)
function LFDTrack () {
function LFD_SearchContainer (requestedContainer) {
return new Promise((resolve, reject) => {
let lfd_scanparams = { TableName: 'lfd_table1' }
db.scan(lfd_scanparams, (err, containers) => {
if (err) {
reject(err);
} else {
containers = containers.Items;
let requestedContainers = []; // different variable than arg
let containerObject; // this will be the resolved object
// this will return the object of the searched container
let findIt = _.forEach(containers, container => {
if (container.container === requestedContainer) {
containerObject = container;
}
});
containerObject = findIt[0];
//console.log(findIt[0]);
resolve(containerObject.container);
}
});
});
}
$(function() {
$("#lfdsubmit").click(function (e) {
e.preventDefault();
let lsd_modaltitle = $("#lfdmodaltitle");
let lsd_modalcontent = $("#lfdmodalcontent");
LFD_SearchContainer('EISU1870725')
.then(container => {
ST2.db2(container); // will send the object
})
.catch(error => {
console.log(error);
});
});
});
}
If ST2.db2(container); returns a promise, you need to change that line to
return ST2.db2(container);
If it doesn't, you can put return null; behind it, like this:
ST2.db2(container);
return null;
Since you didn't provide the definition for ST2, I can't know whether or not the db2 method returns a promise. :)
The error is explained by the author of bluebird here.

Categories