I have started to learn promises and have encountered a problem with sequencing data in my Vue app. Basically, I need to run a few functions in a specific order where each promise must be fully resolved and essential data must be set before the next function is called. I am currently trying to use Promise.all to achieve this but have had no success so far. The promises appear to resolve and fall through to the next function before the data is ready.
Here is an extract of the code.
const promisesArray = [this.loadPrivate(),this.loadPublic()]
await Promise.all(promisesArray).then(() => {
// Promises resolve and fall through to this function before data has been set
this.recordsCheck()
})
.catch((err) => { console.log('ERR IN PROMISE.ALL', err)})
Here is one of the promises. They are both more or less identical. As you can see it's in TypeScript but I don't think that is part of the problem.
async loadPublic(): Promise<TODO> {
new Promise(function(resolve, reject) {
// classes module is a promised-based API call which sets data to an array in a Vuex store
classesModule.getPublic().then((results) => {
// data not ready here yet
console.log('PUBLIC GROUPS IN DASH PROMISE ', results)
resolve() // Resolve triggers before the data is ready
}).catch(e => {
console.log('ERROR in dash.loadGroupClasses : ', e)
reject()
});
});
}
For brevity here is the gist of the classesModule.getPublic function.
#Action async getPublic(): Promise<TODO> {
await getPublicGroups("getPublicGroups", data).then(res => {
// do some work here
this.records.push(...records)
// Records have been correctly set here before the resolve
console.log('Public records array : ', this.records)
resolve()
})
The data definitely gets set eventually because I can see it in the console after the entire page has loaded. It just hasn't finished being set by the time the initial promises resolve and recordsCheck function is called, causing undefined errors.
Maybe there is an issue because of the multiple promises? I've kind of hit an impasse with it and would appreciate any help. Thanks in advance!
UPDATE
OK, based on some of the comments below I could see the problem. The functions in Promises.all were not actually returning promises. Yes, a bit of a silly oversight that I think I missed due to the multiple promises in the chain between different modules. So instead of this..
async loadPublic(): Promise<TODO> {
new Promise(function(resolve, reject) {
classesModule.getPublic().then((results) => {
console.log('PUBLIC GROUPS IN DASH PROMISE ', results)
resolve()
}).catch(e => {
console.log('ERROR in load : ', e)
reject()
});
});
}
I needed to do this...
async loadPublic(): Promise<TODO> {
return classesModule.getPublic().then((results) => {
console.log('PUBLIC GROUPS IN DASH PROMISE ', results)
}).catch(e => {
console.log('ERROR in load : ', e)
});
});
}
It appears to work correctly now. Thanks to all for tips. Really helped me out!
Related
I'm trying to resolve an array of promises in the build process of a NextJS app since I'm getting network errors when using Promise.all, but for some reason I'm having trouble resolving the promises.
This code works, but does not work with my build:
const activityPlacesPromises = activityLocationIDs.map((id) =>
this.get(`places/${id}`)
);
const activityPlaces = await Promise.all(activityPlacesPromises);
console.log(activityPlaces); // Returns the data correctly
This code does not work:
const activityPlaces = activityLocationIDs.reduce((promise, id) => {
return promise.then(() => this.get(`places/${id}`));
}, Promise.resolve());
console.log(activityPlaces); // Returns Promise { <pending> }
Why isn't the Promise.resolve() reduce function working?
PS: I am going off of this SO question: Resolve promises one after another (i.e. in sequence)?
activityPlace is still just a promise you will need to await
console.log(await activityPlaces);
Note that you're not doing anything with the result of each promise (aside from the last one)
Wouldn't it be way easier to just throw this in a regular for loop and await one by one? the reduce pattern is useful for cases where you don't have async/await, but you don't seem to have this limitation:
const results = [];
for(const id of activityLocationIDs) {
results.push(await this.get(`places/${id}`));
}
console.log(result);
The above code matches the behavior of your first sample.
It looks like you are trying to avoid using async/await, firstly perhaps you should catch any errors, to allow logging and execution to continue:
const activityPlaces = activityLocationIDs.reduce((promise, id) => {
return promise
.then(() => this.get(`places/${id}`))
.catch((err) => console.error(`Error getting place ID: ${id}`, err))
}, Promise.resolve());
activityPlaces.then(() => console.log(activityPlaces));
Also, consider that since your code is no longer using async, your build may not wait for the promises to resolve before ending.
I am trying to write a promise in such a way that I can pass some parameters. Inside, this promise will call a Fetch. This is the code I wrote so far:
const myFunction = (parA, parB, parC) => {
return new Promise ((resolve, reject) => {
url = ... // calculated based on the parameters passed above;
fetch(url)
.then(response => {
var object = response.json();
resolve(object);// the idea is that 'object' is returned by the outmost promise
console.log('Check');// So far OK, this is correctly printed to the console
})
.catch(err => {
console.log('Error: ' + err);
reject(err);
});
// resolve('1') // Used for test only. With this, everything works, but "object" here is undefined -- I guess it gets executed too early, before the completion of the Fetch
});
and this is where the promise is called
myFunction(a, b, c).then(res => {
console.log('OK');// just testing
console.log(res);// just testing
});
What happens is that the Fetch resolves OK, but my overall promise doesn't. The last two console.log instructions are never executed.
In short, my problem is: how can I resolve my promise returning the result from the Fetch? It's a promise inside a promise, I guess I could chain them with .then, but I also want to be able to pass parameters.
I also guess I could rewrite the code avoiding this promise chaining, but since I'm also doing it as a way of learning, I prefer to try to figure out first how to solve this problem with this structure.
p.s.: at the moment I am stuck with the promises, cannot use async/await because I am with an old version of node.js and cannot update it
As #VLAZ already mentioned, it's not necessary to create your own promise here, since fetch() itself already returns a promise.
Therefore, you could try this:
const myFunction = (parA, parB, parC) => {
const url = ... // calculated based on the parameters passed above;
return fetch(url)
.then(response => response.json())
.then(object => {
console.log('Check', object);
return object;
})
.catch(err => {
console.log('Error: ' + err);
});
};
From what I understand about promises I should be able to run something in a '.then' after the promise returns and expect that a fetch is completed at that time. But for some reason I cannot get a page to reload AFTER the fetch is done to a node.js app.
fetch("http://localhost:3000/").then(response => {
console.log(response);
}).catch(error => {
console.log(error);
}).then(window.location.reload(false));
I even tried putting a timer on it to see if just needed more time on the server side, but it still runs immediately, ignoring the timer:
.then(function() {
window.location.reload(false)
}, 5000);
Could this be from how the fetch interacts with a node.js app or am I missing something in my implementation?
the problem is that when you set window.location.reload(false) after the then, it is being called at that same moment, in order to wait for the function to trigger, you need to pass an empty function and it will be triggered after.
fetch("https://api.github.com/orgs/nodejs").then(response => {
console.log("we get response")
console.log(response);
}).catch(error => {
console.log(error);
}).then(() => {
console.log("we move...")
// the line is commented so it doesn't loop infinitely.
//window.location.reload(false)
});
what you are doing is something like this:
you are executing the function inmediatly instead of waiting for the resolve.
const newPromise = new Promise((resolve, reject) => {
setTimeout(function() {
console.log('foo')
resolve(true);
}, 1500);
});
newPromise.then(console.log("anything"))
the correct way should be:
const newPromise = new Promise((resolve, reject) => {
setTimeout(function() {
console.log('foo')
resolve(true);
}, 1500);
});
newPromise.then(()=>{
console.log("anything")
})
then expects a function, not a statement. Try .then(() => window.location.reload(false)). A statement will just be executed when evaluated, so you end up with your page reloading (I'm guessing infinitely?)
As we know the concept of "Promise" from ES5 standards in which it makes code non-blocking and has callback function in itself like .then() or .catch() for error handling.
Here you trying to make code sync so here is the way you can use async-await as fetch() return promise resolve or reject to some value unlike
Eg;
const employeeList = async () => {
fetchResult = await fetch("https://api-dev.abc/employees");
return fetchResult;
}
console.log(employeeList());
Await used in async pause then the synchronous flow of execution and wait until promise come with resolved or reject value. Above fetchResult will have either resolved value or rejected value or say error. If we are not using async-await then we have to use .then() callback function for accessing value resolved or rejected by Promise.
Await is used to await callback hell problem in async code of ECMA.
I have been reading up on methods to implement a polling function and found a great article on https://davidwalsh.name/javascript-polling. Now using a setTimeout rather than setInterval to poll makes a log of sense, especially with an API that I have no control over and has shown to have varying response times.
So I tried to implement such a solution in my own code in order to challenge my understanding of callbacks, promises and the event loop. I have followed guidance outlined in the post to avoid any anti-patterns Is this a "Deferred Antipattern"? and to ensure promise resolution before a .then() promise resolve before inner promise resolved and this is where I am getting stuck. I have put some code together to simulate the scenario so I can highlight the issues.
My hypothetical scenario is this:
I have an API call to a server which responds with a userID. I then use that userID to make a request to another database server which returns a set of data that carries out some machine learning processing that can take several minutes.
Due to the latency, the task is put onto a task queue and once it is complete it updates a NoSql database entry with from isComplete: false to isComplete: true. This means that we then need to poll the database every n seconds until we get a response indicating isComplete: true and then we cease the polling. I understand there are a number of solutions to polling an api but I have yet to see one involving promises, conditional polling, and not following some of the anti-patterns mentioned in the previously linked post. If I have missed anything and this is a repeat I do apologize in advance.
So far the process is outlined by the code below:
let state = false;
const userIdApi = () => {
return new Promise((res, rej) => {
console.log("userIdApi");
const userId = "uid123";
setTimeout(()=> res(userId), 2000)
})
}
const startProcessingTaskApi = userIdApi().then(result => {
return new Promise((res, rej) => {
console.log("startProcessingTaskApi");
const msg = "Task submitted";
setTimeout(()=> res(msg), 2000)
})
})
const pollDatabase = (userId) => {
return new Promise((res, rej) => {
console.log("Polling databse with " + userId)
setTimeout(()=> res(true), 2000)
})
}
Promise.all([userIdApi(), startProcessingTaskApi])
.then(([resultsuserIdApi, resultsStartProcessingTaskApi]) => {
const id = setTimeout(function poll(resultsuserIdApi){
console.log(resultsuserIdApi)
return pollDatabase(resultsuserIdApi)
.then(res=> {
state = res
if (state === true){
clearTimeout(id);
return;
}
setTimeout(poll, 2000, resultsuserIdApi);
})
},2000)
})
I have a question that relates to this code as it is failing to carry out the polling as I need:
I saw in the accepted answer of the post How do I access previous promise results in a .then() chain? that one should "Break the chain" to avoid huge chains of .then() statements. I followed the guidance and it seemed to do the trick (before adding the polling), however, when I console logged out every line it seems that userIdApi is executed twice; once where it is used in the startProcessingTaskApi definition and then in the Promise.all line.
Is this a known occurrence? It makes sense why it happens I am just wondering why this is fine to send two requests to execute the same promise, or if there is a way to perhaps prevent the first request from happening and restrict the function execution to the Promise.all statement?
I am fairly new to Javascript having come from Python so any pointers on where I may be missing some knowledge to be able to get this seemingly simple task working would be greatly appreciated.
I think you're almost there, it seems you're just struggling with the asynchronous nature of javascript. Using promises is definitely the way to go here and understanding how to chain them together is key to implementing your use case.
I would start by implementing a single method that wraps setTimeout to simplify things down.
function delay(millis) {
return new Promise((resolve) => setTimeout(resolve, millis));
}
Then you can re-implement the "API" methods using the delay function.
const userIdApi = () => {
return delay(2000).then(() => "uid123");
};
// Make userId an argument to this method (like pollDatabase) so we don't need to get it twice.
const startProcessingTaskApi = (userId) => {
return delay(2000).then(() => "Task submitted");
};
const pollDatabase = (userId) => {
return delay(2000).then(() => true);
};
You can continue polling the database by simply chaining another promise in the chain when your condition is not met.
function pollUntilComplete(userId) {
return pollDatabase(userId).then((result) => {
if (!result) {
// Result is not ready yet, chain another database polling promise.
return pollUntilComplete(userId);
}
});
}
Then you can put everything together to implement your use case.
userIdApi().then((userId) => {
// Add task processing to the promise chain.
return startProcessingTaskApi(userId).then(() => {
// Add database polling to the promise chain.
return pollUntilComplete(userId);
});
}).then(() => {
// Everything is done at this point.
console.log('done');
}).catch((err) => {
// An error occurred at some point in the promise chain.
console.error(err);
});
This becomes a lot easier if you're able to actually use the async and await keywords.
Using the same delay function as in Jake's answer:
async function doItAll(userID) {
await startTaskProcessingApi(userID);
while (true) {
if (await pollDatabase(userID)) break;
}
}
I have a file where I'm writing things:
var stream = fs.createWriteStream("my_file.txt");
stream.once('open', function(fd) {
names.forEach(function(name){
doSomething(name);
});
stream.end();
});
This is working ok and I'm able to write to the file.
The problem is that the doSomething() function has some parts that are asynchronous. An example can be given with the dnsLookup function. Somewhere in my doSomething() I have:
dns.lookup(domain, (err, addresses, family) => {
if(err){
stream.write("Error:", err);
}else{
stream.write(addresses);
}
});
Now, my problem is, since the DNS check is asynchronous, the code keeps executing closing the stream. When the DNS response finally comes it cannot write to anywhere.
I already tried to use the async module but it didn't work. Probably I did something wrong.
Any idea?
Now that NodeJS is mostly up to speed with ES2015 features (and I notice you're using at least one arrow function), you can use the native promises in JavaScript (previously you could use a library):
var stream = fs.createWriteStream("my_file.txt");
stream.once('open', function(fd) {
Promise.all(names.map(name => doSomething(name)))
.then(() => {
// success handling
stream.end();
})
.catch(() => {
// error handling
stream.end();
});
});
(The line Promise.all(names.map(name => doSomething(name))) can be simply Promise.all(names.map(doSomething)) if you know doSomething ignores extra arguments and only uses the first.)
Promise.all (spec | MDN) accepts an iterable and returns a promise that is settled when all of the promises in the iterable are settled (non-promise values are treated as resolved promises using the value as the resolution).
Where doSomething becomes:
function doSomething(name) {
return new Promise((resolve, reject) => {
dns.lookup(domain, (err, addresses, family) => {
if(!err){ // <== You meant `if (err)` here, right?
stream.write("Error:", err);
reject(/*...reason...*/);
}else{
stream.write(addresses);
resolve(/*...possibly include addresses*/);
});
});
});
There are various libs that will "promise-ify" Node-style callbacks for you so using promises is less clunky than the mix above; in that case, you could use the promise from a promise-ified dns.lookup directly rather than creating your own extra one.