Promise.prototype.then() behavior [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have this promise:
var seasonalityArray;
...
aService.gettingAnalysis(site, cohortKey)
.then(function (result) {
let date = moment();
seasonalityArray = result.cooling.getSeasonMonths(date);
})
, function (error) {
    console.error('An error occurred', error);
};
const totalAccounts = _.size(report.asModel().accountNumbers);
const warnings = _.compact(_.map(seasonalityArray, function (monthLabel) {
...
}));
...
aService.gettingAnalysis = function (site, Key) {
return Promise.all([
aService.findingHeat(site, Key),
aService.findingCool(site, Key)
]).spread(function (heating, cooling) {
return { heating: heating, cooling: cooling };
});
};
Inside seasonalityArray variable, it must get an array of months (e.g. ['June','July','August']) which will be used afterwards.
The problem that I found while debugging each step is that after it goes in the line with aService.gettingAnalysis it doesn't enter in .then() and all the code after but it jumps to setting totalAccounts and afterwards it enters in warnings with seasonalityArray being undefined.
Is there a way to make it enter inside then() or at least to set that variable before it is using it as undefined?

aService.gettingAnalysis(site, cohortKey)
.then(function (result) {
let date = moment();
seasonalityArray = result.cooling.getSeasonMonths(date);
//now we can do the rest
const totalAccounts = _.size(report.asModel().accountNumbers);
const warnings = _.compact(_.map(seasonalityArray, function (monthLabel) {}));
})
, function (error) {
    console.error('An error occurred', error);
};
Possible duplicate of how to return from an asynchronous call, so you may read a bit more about Promises.
A Promise is not meant to execute right now, but somewhen. So you cannot simply put code behind it and expect it to execute after the Promise has finished. And you may have a look at async && await ...

If you need the data to be there when you use it you have to use it after your promise resolves.You can move the work you need to be done to a
function.
var seasonalityArray;
...
aService.gettingAnalysis(site, cohortKey)
.then(function(result) {
let date = moment();
seasonalityArray = result.cooling.getSeasonMonths(date);
doStuff();
})
function doStuff() {
const totalAccounts = _.size(report.asModel().accountNumbers);
const warnings = _.compact(_.map(seasonalityArray, function(monthLabel) {
...
}));
}
I'd do away with the "global" and chain promises instead.
aService.gettingAnalysis(site, cohortKey)
.then(function(result) {
let date = moment();
seasonalityArray = result.cooling.getSeasonMonths(date);
return seasonalityArray;
}).then(doStuff)
function doStuff(seasonalityArray) {
const totalAccounts = _.size(report.asModel().accountNumbers);
const warnings = _.compact(_.map(seasonalityArray, function(monthLabel) {
...
}));
}
then returns a promise where the data that get's resolved is what the then returns.

Related

NodeJs javascript: wait until the previous object property is assigned before assigning the next property

Suppose I have code like this:
let result = {
name: downloadNameFromInternetVerySlow(),
isFamous: determineIfNameIsFamous(this.name);
}
const downloadNameFromInternetVerySlow = () => {
path = "/home";
const folders = fs.readdirSync(path).filter(file => fs.lstatSync(path).isDirectory());
console.log(folders);
return folders[0];
}
downloadNameFromInternetVerySlow can take a long time, meanwhile determineIfNameIsFamous depends on downloadNameFromInternetVerySlow's result to return correct value.
How do I make sure determineIfNameIsFamous only runs after downloadNameFromInternetVerySlow is done?
The code you show is entirely synchronous so asynchronous results aren't actually the issue here like people were guessing. The issue is that this.name cannot be used to refer to a prior property in an object literal definition for several reasons, not the least of which this isn't set to the object you want.
Instead, you can do this:
let result = {};
result.name = downloadNameFromInternetVerySlow();
result.isFamous = determineIfNameIsFamous(result.name);
You can convert downloadNameFromInternetVerySlow to a Promise(if not already). and then user await to wait till it finished. Otherwise you can use Promise.then()
(This should be inside an async function)
let name = await downloadNameFromInternetVerySlow(),
let isFamous = determineIfNameIsFamous(this.name);
let result = {
name,
isFamous
}
Another method using Promise.then
let name, isFamous;
downloadNameFromInternetVerySlow().then(result => {
name = result;
isFamous = determineIfNameIsFamous(this.name);
});
Async/await
There’s a special syntax to work with promises in a more comfortable fashion, called “async/await”.
An async function returns a promise, like in this example:
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 3000)
})
}
When you want to call this function you prepend await, and the calling code will stop until the promise is resolved or rejected. One caveat: the client function must be defined as async. Here's an example :
const doSomething = async () => {
console.log(await doSomethingAsync())
}
function downloadNameFromInternetVerySlow(){
return new Promise(resolve => setTimeout(() => {
console.log("Set timeout resolving...");
return resolve("Sample Name");
}, 5000))
}
function determineIfNameIsFamous(name){
if(name === "Sample Name"){
return true;
}
return false;
}
async function myFunc(){
let result = {};
result.name = await downloadNameFromInternetVerySlow();
console.log(result);
result.isFamous = determineIfNameIsFamous(result.name);
return result;
}
myFunc().then(res => console.log("result : ", res))
Assuming downloadNameFromInternetVerySlow is asynchrononous (otherwise it would already work like you want), the solution is to use await.
Please note, to be able to do that, this code needs to be wrapped in an async function (or or top level with top level await available.)
async function getAndUseResult(){
let result = {
name: await downloadNameFromInternetVerySlow(),
isFamous: determineIfNameIsFamous(this.name)
}
console.log(JSON.stringify(result))
}
getAndUseResult();

Having problems with defining a Variable after the result of an API [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I tried to fetch some information through a API and it worked. The next step was to define "ourserver" with the right server data. So I made a foreach and I console-logged just server and got the right one, but if I try to define ourserver = server; the last console log is showing me that ourserver is still undefined.
let ip = "myserverip";
let ourserver;
const fetchPromise = fetch('http://myapi.com/list/servers')
fetchPromise.then(response => {
return response.json();
}).then(list => {
list.forEach(function (server) {
if (server.host === ip) {
ourserver = server;
// console.log(server); this was defined
}
});
});
console.log("daten: " + ourserver); // this is after all still undefined
The reason you are seeing this issue is because your code to define ourserver actually occurs after your console.log("daten: " + ourserver);, you can prove this to yourself by logging the date, too:
ourserver = server;
console.log(server, new Date());
...
console.log("daten: " + ourserver,new Date());
You will see the daten console.log occurs before your log of server.
When you call fetch, it will perform the fetching in the background, and once it has completed, it will call the function provided to then. This is how asynchronous programming works in JavaScript.
Your code that requires ourserver to be present should happen in your then.
Here's your code modified to show how I would go about this:
const ip = "myserverip";
fetch('http://myapi.com/list/servers').then(response => {
return response.json();
}).then(list => {
return list.find(server => server.host === ip);
}).then(ourserver => {
console.log("daten: " + ourserver);
});
If you absolutely must access the value of ourserver outside of the fetch, this can be achieved one of two ways. One way is to define a callback, and the other way is to wait for the value to be set. Here are both ways:
callback
const ip = "myserverip";
let ourserver = null;
const callback = () => {
console.log(ourserver);
}
fetch('http://myapi.com/list/servers').then(response => {
return response.json();
}).then(list => {
ourserver = list.find(server => server.host === ip);
callback();
});
wait
const ip = "myserverip";
let ourserver = null;
fetch('http://myapi.com/list/servers').then(response => {
return response.json();
}).then(list => {
ourserver = list.find(server => server.host === ip);
});
const check = () => {
if(ourserver == null){
return setTimeout(check,100);
}
console.log(ourserver);
};
check();
I do not recommend using the wait and check solution.
read more about Async JS and promise, the reason for this that
fetch promise will run
it's an async operation so the interpreter will not wait until it finish so he will continue execution for the rest of the file.
it will print the console.log
and if the async operation finished the call back will be put in something called event queue, and if the call stack is empty, the event loop will execute your callback function which be the function inside .then in your case.
let ip = "myserverip";
let ourserver;
async function fetchPromise(){
try{
const response = await fetch('http://myapi.com/list/servers');
const list = await response.json();
list.forEach((server) => {
if (server.host === ip) {
ourserver = server;
}
});
console.log("daten: " + ourserver);
}catch(e){
console.log(e);
}
}
You can make use of async/await to handle the fetch better and for a cleaner code. This way you gonna wait until you get the response from the fetch api and then you will have the value of ourserver available.

Async function never stops running

I am running this function with NodeJS, but the process never stops running, despite the fact that I am returning a value once all promises have been resolved.
How can I modify this code so the function terminates when it's done?
let Parser = require('rss-parser');
let parser = new Parser();
var sources = {
feed1: 'http://www.example.url/feed',
feed2: 'http://www.another.url/feed'
}
(async() => {
var promises = [];
for (var feedName in sources) {
if (sources.hasOwnProperty(feedName)) {
const result = parser.parseURL(sources[feedName])
promises.push(result)
}
}
return await Promise.all(promises)
})()
You can take a simple approach handling promises in one array to catch them after parsing each url. Something like this:
let Parser = require('rss-parser');
let parser = new Parser();
var sources = {
feed1: 'http://www.example.url/feed',
feed2: 'http://www.another.url/feed'
}
function parsePromise(url) {
return new Promise((resolve,reject) => {
let data = parser.parseURL(url);
resolve(data);
});
}
// main
toread = [];
for (var feedName in sources) {
let url = sources[feedName];
let p = parsePromise(url);
toread.push(p);
}
Promise.all(toread).then((data) => {
console.log(data);
});
First, I think your async arrow function is incorrect. You are not actually invoking it properly. See the syntax for async arrow functions in this question: Syntax for async arrow function.
Since you are already trying to do an immediate anonymous function, just use the function declaration instead of arrows.
Additionally, you are trying to return something from an anonymous, immediately invoked function and you aren't doing anything with the returned value.
Not only that, but you are awaiting the Promise.all() only to have whatever you waited on get wrapped in a Promise anyway since async functions return Promises no matter what.
Second, use more map()s or intermediate values so you program flows more clearly. The benefit of await is that you can write what looks like synchronous code, but it will be handled in an asynchronous manner.
const Parser = require("rss-parser"),
parser = new Parser();
const sources = {
feed1: "https://sports.yahoo.com/nhl/teams/bos/rss.xml",
feed2: "https://sports.yahoo.com/nhl/teams/buf/rss.xml"
};
(async function () {
let data
try {
const sourcesArr = Object.keys(sources).map(k => sources[k]),
feedPromises = sourcesArr.map(source => parser.parseURL(source));
data = await Promise.all(feedPromises);
} catch (err) {
console.error(err);
}
console.log(data);
}());

Break While loop in NodeJS when Async function is done [duplicate]

This question already has an answer here:
Limited number of javascript promises is not working if running in infinite loop
(1 answer)
Closed 5 years ago.
I have the following node js script:
const rp = require('request-promise')
let result = ''
let asyncdone = false
async function m() {
return await rp.get('http://google.com')
}
m().then((html) => {
result = html
asyncdone = true
}).catch((e) => console.log(e))
while (!(asyncdone)) {
console.log('processing...')
}
console.log(result)
When ran, the loop is infinite.
'processing...' keeps printing even though the async function should have set the asyncdone boolean as true, and thus break the loop, then log the result.
What am I not understanding?
I'll change the code to the following one if you only want to log processing... once.
var statuslogged = false;
while (true) {
if (!statuslogged) {
console.log('processing...');
statuslogged = true;
}
if (asyncdone) {
console.log(result)
}
}
while is actually doing the right thing.
Since it's checking the condition continuously without any delay and getting true everytime, it's printing the info on console, if it does Million checks in a second and the conditions are met, the console will print the string.
So, we need to add a timeout/interval (called pollInterval) so that it checks only after desired time.
I have a different solution for your problem. You want to show progress/a filler text while the promise is going on.
const rp = require('request-promise')
// The base wrapper to stop polluting the environment
async function getHTML(url) {
// A sample filler function
function doSomethingElse() {
console.log(`Processing`);
}
// a filler function to use it later inside
function ensureData({
fillerFn,
pollInterval = 500
}) {
let result;
// grab the data and set it to result async
rp.get(url)
.then((html) => {
result = html
})
.catch((e) => console.log(e))
return new Promise(function(resolve, reject) {
// a self-executing function
(function waitForFoo() {
// if there is result, resolve it and break free
if (result) return resolve(result);
// otherwise run the filler function
fillerFn()
// execute itself again after the provided time
setTimeout(waitForFoo, pollInterval);
})();
});
}
// return or run it
ensureData({
fillerFn: doSomethingElse,
pollInterval: 500
})
.then((result) => {
console.log(result)
})
}
getHTML('http://httpbin.org/ip');

How to get data returned from fetch() promise?

I am having trouble wrapping my head around returning json data from a fetch() call in one function, and storing that result in a variable inside of another function. Here is where I am making the fetch() call to my API:
function checkUserHosting(hostEmail, callback) {
fetch('http://localhost:3001/activities/' + hostEmail)
.then((response) => {
response.json().then((data) => {
console.log(data);
return data;
}).catch((err) => {
console.log(err);
})
});
}
And this is how I am trying to get the returned result:
function getActivity() {
jsonData = activitiesActions.checkUserHosting(theEmail)
}
However, jsonData is always undefined here (which I am assuming is because the async fetch call has not finished yet when attempting to assign the returned value to jsonData. How do I wait long enough for the data to be returned from the fetch call so that I can properly store it inside of jsonData?
always return the promises too if you want it to work:
- checkUserHosting should return a promise
- in your case it return a promise which return the result data.
function checkUserHosting(hostEmail, callback) {
return fetch('http://localhost:3001/activities/' + hostEmail)
.then((response) => {
return response.json().then((data) => {
console.log(data);
return data;
}).catch((err) => {
console.log(err);
})
});
}
and capture it inside .then() in your main code:
function getActivity() {
let jsonData;
activitiesActions.checkUserHosting(theEmail).then((data) => {
jsonData = data;
}
}
EDIT:
Or even better, use the new syntax as #Senthil Balaji suggested:
const checkUserHosting = async (hostEmail, callback) => {
let hostEmailData = await fetch(`http://localhost:3001/activities/${hostEmail}`)
//use string literals
let hostEmailJson = await hostEmailData.json();
return hostEmailJson;
}
const getActivity = async () => {
let jsonData = await activitiesActions.checkUserHosting(theEmail);
//now you can directly use jsonData
}
You're partially right. It's because you're trying to get the result of this asynchronous call in a synchronous fashion. The only way to do this is the same way you deal with any other promise. Via a .then callback. So for your snippet:
function getActivity() {
return activitiesActions.checkUserHosting(theEmail).then((jsonData) => {
// Do things with jsonData
})
}
Any function that depends on an asynchronous operation must itself become asynchronous. So there's no escaping the use of .then for anything that requires the use of the checkUserHosting function.
You can make use of new ES6 and Es7 syntax and what others have written is also correct, but this can be more readable and clean,
you are trying to get aysnc value synchronously, here jsonData will be undefined because, you move to next line of execution before async function(checkUserHosting) is finish executing, this can be written as follows
const getActivity = async () => {
let jsonData = await activitiesActions.checkUserHosting(theEmail);
//now you can directly use jsonData
}
and you can write checkUserHosting in a different using new syntax like this
const checkUserHosting = async (hostEmail, callback) => {
let hostEmailData = await fetch(`http://localhost:3001/activities/${hostEmail}`)
//use string literals
let hostEmailJson = await hostEmailData.json();
return hostEmailJson;
}

Categories