Returning Promise says Promise Pending, Node js? - javascript

I am new to Nodejs and first time working on promises so now the context is when I try to return promise it shows status Promise . How to fix it can anyone guide me through this?
Here is the code where I am calling a function that will return a promise. bold line showing where I want to return that promise and store in an object.
for(let i = 0; i<responseArray.length; i++){
let dollar = {
amount : 0
};
if(i == 1){
continue;
}
dollar.amount = **currenciesService.getCurrencyLatestInfo(responseArray[i].currency);**
dollarAmount.push(dollar);
}
console.log("$", dollarAmount);
Here is a code which is returning promise.
const getCurrencyLatestInfo = function(currency) {
return new Promise(function(resolve, reject) {
request('https://min-api.cryptocompare.com/data/price?fsym='+currency+'&tsyms='+currency+',USD', { json: true }, (err, res, body) =>
{
if (err) {
reject(err);
} else {
var result= body;
resolve(result);
console.log("RESULT: ",result.USD);
}
});
})
}

You'll need to wait for those promises to resolve before you can use the resolved values
here is a small rewrite of your loop that should work
let promises = [];
for(let i = 0; i<responseArray.length; i++){
if(i == 1){
continue;
}
let dollar = currenciesService.getCurrencyLatestInfo(responseArray[i].currency)
.then(amount => ({amount})); // do you really want this?
promises.push(dollar);
}
Promise.all(promises)
.then(dollarAmount =>console.log("$", dollarAmount))
.catch(err => console.error(err));
This should result in an array like [{amount:123},{amount:234}] as your code seems to expect
The above can also be simplified to
Promise.all(
responseArray
.filter((_, index) => index != 1)
.map(({currency}) =>
currenciesService.getCurrencyLatestInfo(currency)
.then(amount => ({amount})) // do you really want this?
)
)
.then(dollarAmount =>console.log("$", dollarAmount))
.catch(err => console.error(err));
Note: your original code suggests you want the results to be in the form {amount:12345} - which seems odd when you want to console.log("$", ....) ... because the console output would be something like
$ [ { amount: 1 }, { amount: 0.7782 } ]
given two results of course - can't see your responseArray so, am only guessing

Related

How to stop a js promise.all() on a condition?

I have this piece of code
let promiseList = []
for (let i in data) {
let promise = checkIfMember(data[i].tg_id, chatId).then(res => {
if (res) {
//if the user has a undefined username it won't be rained
if (data[i].username != "undefined") {
console.log(data[i].username)
members.push([data[i].tg_id, data[i].username])
}
}
}).catch(err => {
console.log(err)
})
promiseList.push(promise)
}
return Promise.all(promiseList).then((res) => {
//list of members that are in the room , we randomize it before returning
shuffleArray(members)
if(numberOfUsers > members.length){
return[false, members.length]
}else{
members = members.slice(0,numberOfUsers)
return [true, members]
}
});
Basically I have a promise list that is being filled. And then all of them are executed with a promiseAll. The problem is that I dont want all of them to execute, I want to to do something like this:
let promiseList = []
let validmembers = 0;
for (let i in data) {
let promise = checkIfMember(data[i].tg_id, chatId).then(res => {
if (res) {
//if the user has a undefined username it won't be rained
if (data[i].username != "undefined") {
console.log(data[i].username)
members.push([data[i].tg_id, data[i].username])
//stop there the execution
validmembers++;
if(validmembers == numberOfUsers){
return;
}
}
}
}).catch(err => {
console.log(err)
})
promiseList.push(promise)
}
return Promise.all(promiseList).then((res) => {
//list of members that are in the room , we randomize it before returning
shuffleArray(members)
if(numberOfUsers > members.length){
return[false, members.length]
}else{
members = members.slice(0,numberOfUsers)
return [true, members]
}
});
But the problem is that the promises are async, so the promiselist filling doesnt stop.
How would I solve this?
It appears you want the members array to have no more than numberOfUsers entries in it due to the promises. Since the promises run asynchronously you can't stop the checkIfMember function from being called, but inside of its then function you could avoid accumulating more members like this:
// don't use `!== "undefined"` unless it can actually be the string "undefined"
if (data[i].username) {
if (members.length < numberOfUsers) {
console.log(data[i].username);
members.push([data[i].tg_id, data[i].username]);
}
}
By only pushing to members when it's not yet full you will avoid adding more than numberOfUsers and don't need to do the slice later.
If instead you want to avoid the entire promise work when enough have been collected, then you can serialize them like this:
async function yourFunction() {
for (let i in data) {
if (members.length < numberOfUsers) {
// don't continue to the next 'i' until this one is done
await checkIfMember(data[i].tg_id, chatId)
.then(res => {
if (res) {
//if the user has a undefined username it won't be rained
if (data[i].username != "undefined") {
console.log(data[i].username);
members.push([data[i].tg_id, data[i].username]);
}
}
})
.catch(err => {
console.log(err);
});
} else {
break; // no need to continue once members is full
}
}
return [true, members];
}

Cannot access array elements outside for loop

The problem:
When I try to console log the array inside the loop it shows me that the result was added to the array. However, when outside the loop, I get an empty array on console.log
var ResultArray01= [];
for(var gg=0; gg<ResultArray.length;gg++) // ResultArray is come from another function
{
IPFS.get.call(ResultArray[gg],function(error, result) { //this function is about smart contract
if (error)
{
console.log(error);
}
else
{
ResultArray01[gg] = result; //get the result, and store it into ResultArray01
console.log(ResultArray01[gg]); //it can successfully print the value
}
});
}
console.log(ResultArray01); //returns empty array
Can someone help me? thanks
As #jfriend00 mentioned in the comment. Your console.log() before the callback is being executed. You can make use of promises here to handle such a scenario.
This is how you can acheive the desired result.
var ResultArray01 = [];
var promises = [];
for (var gg = 0; gg < ResultArray.length; gg++) {
promises.push(
new Promise((resolve, reject) => {
IPFS.get.call(ResultArray[gg], function (error, result) {
if (error) {
console.log(error);
// reject promise in case there is any error.
reject(error)
} else {
//get the result, and store it into ResultArray01
ResultArray01[gg] = result;
// it can successfully print the value
console.log(ResultArray01[gg]);
// resolve if everything is as expected.
resolve();
}
})
})
);
}
Promise.all(promises).then(() => {
console.log(ResultArray01); // it should print the desired result now.
}).catch((err) => console.log(err))

How to know if a promise inside a loop has ended?

First of all, I'm sorry if this is a basic question, but I am not being able to do this.
I have a promise that inserts data into a table (sqlite), and I have a for iterating over an array. I want to put all of that data into the table, but I want to know when it ends, to display a message at the end. I was verifying (i == array.length -1) it displayed the message but that doesn't seem correct. If I don't do this in this way the message displays before it has ended.
I have two other promises that should run along this one and the solution(it seems a bad solution) above wouldn't work in this case, because one can end after or before the iteration. How can I know when they all are done too?
Could you help me, please?
Here's my code:
for (let i = 0; i < this.data_array.length; i++) {
this.database.insertService(this.data_array[i]).then(async () => {
console.log('Inserting object number ' + this.data_array[i].id);
if( i == this.data_array.length - 1) {
console.log('done!');
}
}).catch(err => console.log('error inserting object into the table'));
}
Thank you.
It is a better practice to use Promise.all when dealing with such cases. A simple example would be the following:
let i;
let promises = [];
for (i = 0; i < 5; ++i)
promises.push(someAsyncFunc(i));
Promise.all(promises)
.then((results) => {
console.log("All done", results);
})
.catch((e) => {
console.log(e)
});
You can solve this using Promise.all
Here is an example and here some useful documentation
the example is using map instead of a for loop to iterate over the array.
I haven't tested it but that should work
Promise.all(
this.data_array.map((data) => {
console.log('Inserting object number ' + data.id);
return this.database.insertService(data);
})
)
.then((result) => console.log('done', result))
.catch((error) => console.log(error));
You could use Promise.all([]) where .all() take an array.
Promise.all(
// Map the items in data_array to the async insertService function
this.data_array.map(dataEntry => this.database.insertService(dataEntry))
).then((resultArray) => {
console.log(resultArray.length);
console.log('done!');
}).catch((e) => {
console.log(e)
});
I think that Promise.all is the best solution here
async method() {
const promises = this.data_array.map(item =>
this.database.insertService(item));
try {
const result = await Promise.all(promises);
console.log({ result });
} catch (error) {
console.log({ error });
}
}

Javascript - Chaining 2 (or more) arrays of promises

I have some code that does this: First scrape this array of webpages. After that, scrape another array of webpages.
The following code does what I expect:
let bays=[];
let promises=promisesN=[];
for (let y=2019;y>=2015;y--)
promises.push(new Promise(resolve=>
curl.get(`/*url*/${y}.html`,null, (error,resp,body)=>
resp.statusCode==200? resolve(parse(body)):reject(error)
)));
Promise.all(promises).then(()=>{
bays.forEach(bay=>{
if (bay.no.match(/\d+/)<=103) return;
promisesN.push(new Promise(resolve=>
curl.get(`/*url*/${bay.code}/`,null, (error,resp,body)=>
resp.statusCode==200? resolve(image(bey,body)):reject(error)
)))});
Promise.all(promisesN).then(()=>{
bays.sort((a,b)=>{return parseInt(a.no.match(/\d+/))<parseInt(b.no.match(/\d+/))? -1:1});
console.log(bays);
});
}).catch(error=>console.log(error));`
So I've read you can write a simplier nesting-free syntax:
doSomething()
.then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
How to apply this to the code above?
correctness
let promises=promisesN=[];
This is really incorrect. It makes both variables reference the same array, and makes promisesN an implicit global. The fact that it appears to work means you aren’t in strict mode. Always use strict mode. The correct version of what you intended is:
let promises = [];
let promisesN = [];
cleanliness
new Promise(resolve=>
curl.get(`/*url*/${y}.html`,null, (error,resp,body)=>
resp.statusCode==200? resolve(parse(body)):reject(error)
))
You’re repeating this pattern, so make it into a function, or use a package that does the job for you, like request-promise[-native] or axios. (Also, please show your real code. reject isn’t defined here.)
const getAsync = url => new Promise((resolve, reject) => {
curl.get(url, null, (error, resp, body) => {
if (resp.statusCode === 200) {
resolve(body);
} else {
reject(error);
}
});
});
Notice how you’re free to make the function more readable when it isn’t repeated, and to extend it later.
let promises = [];
let promisesN = [];
for (let y = 2019; y >= 2015; y--) {
promises.push(getAsync(`/*url*/${y}.html`).then(parse));
}
Promise.all(promises).then(bays => {
bays.forEach(bay => {
if (bay.no.match(/\d+/) <= 103) return;
promisesN.push(getAsync(`/*url*/${bay.code}/`).then(body => image(bay, body)));
});
Promise.all(promisesN).then(() => {
bays.sort((a, b) => {return parseInt(a.no.match(/\d+/)) < parseInt(b.no.match(/\d+/)) ? -1 : 1;});
console.log(bays);
});
}).catch(error => console.log(error));
I had to take a few guesses at what your real code looks like again, because you’re surely doing something with the resolved value of Promise.all(promises). It doesn’t have any easily-accessible side-effects. bey also seemed likely enough to be bay.
Now you can give promisesN a more appropriate scope:
let promises = [];
for (let y = 2019; y >= 2015; y--) {
promises.push(getAsync(`/*url*/${y}.html`).then(parse));
}
Promise.all(promises).then(bays => {
let promisesN = bays
.filter(bay => bay.no.match(/\d+/) > 103)
.map(bay => getAsync(`/*url*/${bay.code}/`).then(body => image(bay, body)));
Promise.all(promisesN).then(() => {
bays.sort((a, b) => {return parseInt(a.no.match(/\d+/)) < parseInt(b.no.match(/\d+/)) ? -1 : 1;});
console.log(bays);
});
}).catch(error => console.log(error));
and use an expression-bodied arrow function where appropriate, since you’re already using them whenever they aren’t appropriate:
bays.sort((a, b) => parseInt(a.no.match(/\d+/)) < parseInt(b.no.match(/\d+/)) ? -1 : 1);
Now, if my guess about bays is right, then you can’t unnest. If it comes from somewhere else then you can. Normally I would leave a comment about that but I already wrote all this, so… please clarify that for further cleanup.
If you're looking to simplify your code, you might consider the use of async/await instead of promises.
The async/await syntax will greatly simplify the presentation and ease comprehension of the code, especially given that your logic relies on asynchronous iteration of arrays.
Consider the following code revision of your code:
/* Define local helper that wraps curl() in async function declaration */
function async doRequest(url) {
return (await new Promise(resolve=> curl.get(url, null, (error,resp,body) =>
resp.statusCode==200 ? resolve(res) : reject(error))))
}
/* Re-define simplified scrape logic using await/async */
function async doScrape() {
try {
var bays = []
/* Iterate date range asynchronously */
for (let y=2019; y>=2015; y--) {
/* Use doRequest helper function to fetch html */
const response = await doRequest(`/*url*/${y}.html`)
const bay = parse(response)
bays.push(bay)
}
/* Iterate bays array that was obtained */
for(const bay of bays) {
/* Use doRequest helper again to fetch data */
const response = await doRequest(`/*url*/${bay.code}/`)
/* Await may not be needed here */
await image(bay, response)
}
/* Perform your sort (which is non asynchronous) */
bays.sort((a,b)=> parseInt(a.no.match(/\d+/))<parseInt(b.no.match(/\d+/))? -1:1);
console.log("Result", bays);
}
catch(err) {
/* If something goes wrong we arrive here - this is
essentially equivalent to your catch() block */
console.error('Scrape failed', err);
}
}
/* Usage */
doScrape()
Hope that helps!
Not entirely sure if this is what you want, but I've separated your code out a bit because I found it easier for me to read.
let bays = [];
let promises = [];
let promisesN = [];
for (let y = 2019; y >= 2015; y--) {
const promiseOne = new Promise((resolve, reject) => {
return curl.get(`/*url*/${y}.html`, null, (error, resp, body) => {
resp.statusCode === 200 ? resolve(parse(body)) : reject(error);
});
});
promises.push(promiseOne);
}
Promise.all(promises)
.then(() => {
bays.forEach((bay) => {
if (bay.no.match(/\d+/) <= 103) {
return;
}
const promiseTwo = new Promise((resolve, reject) => {
return curl.get(`/*url*/${bay.code}/`, null, (error, resp, body) => {
resp.statusCode === 200 ? resolve(image(bay, body)) : reject(error);
});
});
promisesN.push(promiseTwo);
});
return Promise.all(promisesN);
})
.then(() => {
bays.sort((a, b) => {
return parseInt(a.no.match(/\d+/), 10) < parseInt(b.no.match(/\d+/), 10) ? -1 : 1;
});
console.log(bays);
})
.catch((error) => {
console.log(error);
});
I am wondering though, you are firing the promises instantly on each iteration of your for loop. This might be intentional, but it means if those promises resolve before the code gets to execute Promise.all you may run into issues. I personally would do something like, e.g. const promiseOne = () => somePromise, that way you can create a bunch of promises, and then once they're all created, map over that array and fire them at once. Same thing goes for the second promises.
Not sure if this is helpful, let me know if it is. Feel free to ask more questions too.

Synchronize multiple Promises while allowing multiple number of retries

I am trying to build a downloader that automatically retries downloading. Basically, a task queue which retries tasks for a certain number of times. I first tried using Promise.all() but the "trick" to circumvent the fail-on-first-reject described here did not help (and is an anti-pattern as described further down in that thread)
So I got a version working which seems to somewhat do what I want. At least the results it prints are correct. But it still throws several uncaught exception test X errors/warnings and I don't know what to do about that.
The Code:
asd = async () => {
// Function simulating tasks which might fail.
function wait(ms, data) {
return new Promise( (resolve, reject) => setTimeout(() => {
if (Math.random() > 0.5){
resolve(data);
} else {
reject(data);
}
}, ms) );
}
let tasks = [];
const results = [];
// start the tasks
for ( let i = 0; i < 20; i++) {
const prom = wait(100 * i, 'test ' + i);
tasks.push([i, prom]);
}
// collect results and handle retries.
for ( let tries = 0; tries < 10; tries++){
failedTasks = [];
for ( let i = 0; i < tasks.length; i++) {
const task_idx = tasks[i][0];
// Wait for the task and check whether they failed or not.
// Any pointers on how to improve the readability of the next 6 lines appreciated.
await tasks[i][1].then(result => {
results.push([task_idx, result])
}).catch(err => {
const prom = wait(100 * task_idx, 'test ' + task_idx);
failedTasks.push([task_idx, prom])
});
}
// Retry the tasks which failed.
if (failedTasks.length === 0){
break;
} else {
tasks = failedTasks;
}
console.log('try ', tries);
}
console.log(results);
}
In the end, the results array contains (unless a task failed 10 times) all the results. But still uncaught exceptions fly around.
As not all rejected promises result in uncaught exceptions, my suspicion is, that starting the tasks first and applying then()/catch() later is causing some timing issues here.
Any improvements or better solutions to my problems are appreciated. E.g. my solution only allows retries "in waves". If anyone comes up with a better continuous solution, that would be much appreciated as well.
Using await and asnyc allows to solve that in a much clearer way.
You pass an array of tasks (functions that when executed start the given task) to the execute_tasks. This function will call for each of those tasks the execute_task, passing the task function to it, the execute_task will return a Promise containing the information if the task was successful or not.
The execute_task as a loop that loops until the async task was successful or the maximum number of retries reached.
Because each of the tasks has its own retry loop you can avoid those waves. Each task will queue itself for a new execution as it fails. Using await this way creates some kind of cooperative multitasking. And all errors are handled because the task is executed in a try catch block.
function wait(ms, data) {
return new Promise((resolve, reject) => setTimeout(() => {
if (Math.random() > 0.5) {
resolve(data);
} else {
reject(new Error());
}
}, ms));
}
async function execute_task(task) {
let result, lastError;
let i = 0
//loop until result was found or the retry count is larger then 10
while (!result && i < 10) {
try {
result = await task()
} catch (err) {
lastError = err
// maybe sleep/wait before retry
}
i++
}
if (result) {
return { success: true, data: result }
} else {
return { success: false, err: lastError }
}
}
async function execute_tasks(taskList) {
var taskPromises = taskList.map(task => execute_task(task))
// the result could be sorted into failed and not failed task before returning
return await Promise.all(taskPromises)
}
var taskList = []
for (let i = 0; i < 10; i++) {
taskList.push(() => {
return wait(500, {
foo: i
})
})
}
execute_tasks(taskList)
.then(result => {
console.dir(result)
})

Categories

Resources