Promise inside promise don't wait Axios finishing - javascript

I have a trouble. I receive many objects where I mapping them and I make a external consult using axios and save the return, let's the code:
let savedClients = Object.entries(documents).map(personDocument => {
let [person, document] = personDocument
documentFormated = document
documentNumbers = document.replace(/\D/g, '')
return ConsultDocuments.getResponse(documentNumbers).then(resultScore => { // Calling the axios
const info = { ...resultScore }
return Save.saveClient(info)
})
})
Promise.all(savedClients).then(results => {
console.log(results) // Come only one document, repeted with the total documents passed in map
})
The problem is when it realized the all map first and then make the consults with only the last result many time (the total of documents passed)
This code is legacy and use async/await don't work (serious, if i don't stay here)
I'am tried N ways to make this, and with the libary Q(), it's make the map in correcty order but it's doesn't wait the axios, and all results come with "pending"
Thanks!

Related

How do I append the results from an async await promise to a global array?

What I am trying to do: Pull data from airtable and put it into an array so that the items within this array can be mapped to an html table later on.
The issue I am having: Javascript pulls data from airtable in an asynchronous way (or so I understand it as such) so no matter if I use an async await function or just a normal one I am always getting a promise returned. All I want to do is to pull these items from the table and put them in an array but it is proving to be more difficult than it should be. (Still new to javascript, so it could just be that I am not understanding everything fully yet).
Code to pull the data:
var Airtable = require('airtable');
var base = new Airtable({apiKey: 'API-KEY'}).base('BASE-ID');
const table = base('BASE-NAME');
let lst = [];
const getRecords = async () => {
try {
const records = await table.select().all();
for(i = 0; i < records.length; i++){
lst.push(records[i])
}
} catch (err) {
console.error(err);
}
}
I know the above code works because if I switch out the line where I am pushing the data to the lst with a console.log(records[i]) I get the correct data item am trying to push to the lst printed to the console. The issue arises when I try to run a console.log(lst). It just returns Promise {pending}.
So, what I happened to come across in the developer doc and here on stack overflow is that I need to use a .then() in order to return the lst so, I tried the following:
getRecords().then(res => {
console.log(lst)
}
);
which did indeed give me the desired results I was looking for but now if a run a console.log(lst) I get the same Promise {pending} message printed to the console.
Does this mean I will have to map the items within lst to the HTML table within the
getRecords().then(res => {
console.log(lst) // so instead of printing the lst to the console here, would I just map the items to the html table?
}
);
section of the code?

How to use if else loop in Dialogflow for showing results

I have Javascript code for Dialogflow doing project on Google Actions. For this code if answer is in database means it will answer otherwise it is exiting out of app. So, I want to use else loop for this code plz help me
function handleCompanyDetails(agent){
const RegNo = agent.parameters.RegNo;
var ref8 = admin.database().ref().child("Table/");
var query8 = ref8.orderByChild("RegNo").equalTo(RegNo);
return query8.once("value")
.then(function(snapshot) {
snapshot.forEach(function(child) {
if( !snapshot.exists() ){
// There are no results, say so
agent.add("There are no results for that account.");
} else {
// ... Do something with the data
agent.add(`The student placed in ` + child.val().CompanyName);
}
});
});
}
While you can use a loop to show results, there are a few problems with how you've done to, and possibly even with what you're trying to return.
First - Dialogflow requires you to return a Promise from any function that makes an asynchronous call, such as a call to the Firebase database. You're currently using the callback method. You should switch to using once() that returns a Promise instead, so that might look something like this:
return query8.once("value")
.then( snapshot => {
// working with snapshot goes here
})
.catch( err => {
console.error(err);
agent.add("There was a problem.");
});
The second is how you work with snapshot itself. If you're expecting multiple results, you should be aware that you can only call agent.add() with a text message twice and one basic card. If you want multiple cards, you may want to use a list or carousel instead.
If you are expecting only one response indexed off the RegNo, which it looks like you may be, then you should just include that as part of the path and get the value of the snapshot. You wouldn't need a loop in this case.
Update based on updated code.
As you noted, you're not sending anything if there are no results, so the Action exits with an error.
The easiest way to do this is to use snapshot.exists() to check if there are any results in the snapshot. If there aren't any, then you can return an error. That might look something like
return query8.once("value")
.then(function(snapshot) {
if( !snapshot.exists() ){
// There are no results, say so
agent.add("There are no results for that account.");
} else {
// ... Do something with the data
}
});
// ...
If you do have results, you still have the issue that you may be sending back too many replies. You can only have one agent.add() with a message to be spoken (or two, at the most, but don't do that) and only one Card, unless you use a List or Carousel. So it would be better for you to build that message inside the loop.
Use .then() while doing operations on snapshot. Because .once() only triggers one time so if data is available meaning.then() will be executed and you can exit it using .catch(). check the code below.
function handleCompanyDetails(agent){
const RegNo = agent.parameters.RegNo;
var ref8 = admin.database().ref().child("Table/");
var query8 = ref8.orderByChild("RegNo").equalTo(RegNo);
return query8.once("value")
.then(function(snapshot) {
snapshot.forEach(function(child) {
agent.add(`The student placed in ` + child.val().CompanyName);
agent.add(new Card({
title: ` Name:${child.val().Studentname}
Reg No: ${child.val().RegNo}
Offer Date: ${child.val().OfferDate} `,
imageUrl: '',
text: `Thanks for using πŸ’\n ${child.val().FirstName} πŸ’`,
buttonText: '.com'
})
})
})
.catch( // throw some error)
}
you can read more here, https://firebase.google.com/docs/database/web/read-and-write

How to wait for all dynamic number of forks to complete with redux-saga?

I'm trying to use redux saga to query a number of rest endpoints to get a human readable name associated with each discovered network, to fill a dropdown list with selectable networks.
I'm having trouble doing this, my forking is not working. I keep getting the error message:
TypeError: __webpack_require__.i(..) is not a function(...)
Every example using all() I've found online use call and know ahead of time every request being made. However, judging from the API I tried something like this:
const pendingQueries = [];
for(networkId in discoveredNetworks) {
pendingQueries.push(fork(getApplicationName, networkId);
}
const queryResults = yield all(pendingQueries);
This failed. I've tried a number of other permutations since. From testing I'm able to verify that I can do this:
const results = [];
for(networkId in discoveredNetworks) {
results.push(yield fork(getApplicationName, networkId));
}
and if There is a long enough delay the method will run and complete, though this approach obviously doesn't gaurentee that the forked methods will complete before I use result as I want. Still it seems to confirm the problem is in my use of all.
What is wrong with my all command?
Why don’t you wrap each request in a promise and call them like so:
var promises = []
for(networkId in discoveredNetworks) {
promises.push(new Promise((res, rej) => {
// code from getApplicationName goes here
// call res(result) on success and rej(error) on failure
}));
}
const results = yield call(Promise.all(promises))
I got this to work by giving up on the all() method, which I never got to work as advertised but wasn't really the right method for the job.
For forks I should have been using join() instead. so something along the lines of this:
const pendingQueries = [];
for(networkId in discoveredNetworks) {
pendingQueries.push(yield fork(getApplicationName, networkId);
}
const results = yield join(...pendingQueries);
results.forEach((result) => {
// my logic for handling the response and generating action
}

Make DRY code from Async multiple functions inside array of Promise.all

Hello the following code is working. However, I was wondering if there is a way to not repeat the function findMovie in the array which is asynchronous multiple times as I'm currently doing.
var request = require('request-promise');
function findMovie(title){
return request(`http://www.omdbapi.com/?t=${title}`)
.then(res=>{
var movie= JSON.parse(res)
return [movie["Title"], movie["Year"],movie["Genre"]]
})
}
function loadInitialData(movies){
return Promise.all(movies)
.then((response)=>{
response.forEach((movie)=>{
console.log(movie[0])
})
})
}
var movies= [findMovie("jason bourne"), findMovie("The Matrix"), findMovie("titanic")];
// Above is the function findMovie being written multiple times to make it work...
loadInitialData(movies);
You can use map to run a function on every element of an array and return a new array. So you can have a list of strings, and map it to a list of movies.
var movies= ["jason bourne", "The Matrix", "titanic"].map(function (movie) {
return findMovie(movie);
});
Now because your function takes a single parameter, you can just pass the function name to map, which neatens it up further
var movies= ["jason bourne", "The Matrix", "titanic"].map(findMovie)
This doesn't have anything to do with your code and everything to do with whether the API you're calling supports batch requests. If it did, then you could pass all the titles at once in a single request, get a JSON object back with an array of movies in it. Again, only if it support that.
If it's any consolation, all the requests that your findMovie() function are doing will be done in parallel, so they'll be faster than making sequential requests for each movie.
For completeness, this isn't really what DRY (Don't Repeat Yourself) means. DRY refers to not writing the same code over and over when it could be put into a function. If your code wasn't DRY, you wouldn't have the findMovie() and would instead have multiple separate calls to request() in your main code body.
Here's probably the way I'd write it
const request = require('request-promise')
const findMovie = title =>
request(`http://www.omdbapi.com/?t=${title}`)
.then(JSON.parse)
.then(({Title, Year, Genre}) => [Title, Year, Genre])
const findMovies = titles =>
Promise.all(titles.map(findMovie))
findMovies(["jason bourne", "The Matrix", "titanic"])
.then(movies => console.log(movies),
err => console.error(err.message))

Functional promise using jQuery

I am trying to download some data from a Phoenix web app and render the data on the front-end. For this I have the request and callback in a list and am running a list.reduce against it while thening each request.
var Database = function() {
this.reservations = [];
this.applications = [];
this.environments = [];
};
var database = new Database();
var requests = [$.getJSON('/api/applications', data => { database.applications = data }),
$.getJSON('/api/environments', data => { database.environments = data }),
$.getJSON('/api/reservations', data => { database.reservations = data })];
function run() {
requests.reduce(function(chain, callback) {
return (chain ? chain.then(callback) : callback);
}, null).then(() => render(database));
}
However, this works most of the time in latest version of Google Chrome and maybe 10% of the times in Safari.
When I inspect the "database" after stepping into the render function, I see list of applications, but not environments and reservations (2 pieces of data).
Edit: Okay, so it works in Google Chrome in normal mode. But does not work all the time in incognito mode. In Safari it sometimes fetches all 3 pieces of data, and sometimes just 2 pieces of data. My application does not use any sessions.
I am guessing this is caused by the nature of $.ajax being asynchronous and perhaps I broke my promises. But I have hit a roadblock.
Any insights?
You don't need to chain your promises, they can all be requested at the same time since it doesn't look like any of them rely on each other. Instead, you can use a .map() and take the result, which will be a list of promises, and use an .all() to wait for them all to resolve.
(I'm taking some liberties so the following should be considered pseudo-code)
var urls = ['whatever.com', 'example.com/something', 'something.com/whatever'];
var requestPromises = urls.map(function(url) {
return ajax(url); // assume ajax() returns a promise
});
Promise.all(requestPromises).then(function(results) {
console.log(results);
});
Thanks #Soviut for your help. For closure: here's what I did to fix this problem.
// ... is the ES6 spread operator
function run() {
$.when(...requests).then(() => {
render(database);
});
}

Categories

Resources