Hello,
I use Promise for a initialize node project.
I want to insert in my MongoDb the name of files on all of my branch on my git repository.
I use nodegit to manipulate repo, for every nodegit method the return is a Promise. But i need to loop on all branch reference for get all files and branch name.
After that i can prepare my array for insert in database on next promise.
Code look like :
// List all branchs
.then((branchs) => {
let promises = [];
let allBranchFiles = [];
branchs.map((branch) => {
let q = repo.checkoutBranch(branch, config.checkoutOpts)
.then(() => repo.getCurrentBranch())
.then((ref) => {
return new Promise((resolve, reject) => {
fs.readdir('./myRepo/', (err, files) => {
files.map((file) => {
allBranchFiles.push({fileName: file, branch: branch});
});
resolve(allBranchFiles);
});
});
});
promises.push(q);
});
return Promise.all(promises);
})
That code finish by two way :
First :
{ Error: the index is locked; this might be due to a concurrent or crashed process
at Error (native) errno: -14 }
I'm sure no other process use git in this repo!
Second :
All of my files get the same value for "branch" but they are on separate branchs.
Thank you for helping me guys !
Cya.
I try something and it's work but it's ugly.
I mean, I use a recursiv function for doing my stuff. I wait better for change that.
I let you know how I replace my code on the question.
// List all branchs
.then((branchs) => {
return new Promise((resolve, reject) => recursiv(branchs).then(() => resolve(allBranchFiles)));
})
And my function :
let recursiv = (branchs, index=0) => {
return new Promise((resolved, rejected) => {
repo.checkoutBranch(branchs[index], config.checkoutOpts)
.then(() => repo.getCurrentBranch())
.then((user) => {
return new Promise((resolve, reject) => {
fs.readdir('./myRepo/', (err, files) => {
files.map((file) => {
allBranchFiles.push({fileName: file, branch: branchs[index]});
});
resolve();
});
});
})
.done(() => {
if (index < branchs.length-1) resolved(recursiv(branchs, index+1));
else resolved();
});
});
};
Related
I'm currently trying to send 2 objects to the front .hbs front end. However I cant seem to work out how to do this because I'm using promises.
Currently, my thinking is i perform the sql query, the country and organisation name is extracted, and then each sent to a geocoding api, returned and then squashed together in the same promises. But i'm not sure how to extract these for the render function.
Node
//route for homepage
app.get('/', (req, res) => {
let sql = "SELECT org_name, country_name from places;
let query = conn.query(sql, (err, results) => {
if (err) throw err;
const geoPromise = param => new Promise((resolve, reject) => {
geo.geocode('mapbox.places', param, function(err, geoData) {
if (err) return reject(err);
if (geoData) {
resolve(geoData.features[0])
} else {
reject('No result found');
}
});
});
const promises = results.map(result =>
Promise.all([
geoPromise(result.country_name),
geoPromise(result.org_name)
]));
Promise.all(promises).then((geoLoc, geoBus) => {
res.render('layouts/layout', {
results: JSON.stringify(geoLoc),
businesses: JSON.stringify(geoBus)
});
});
});
});
Front end call
results1 = {{{results}}}
console.log(results1.length)
business1 = {{{businesses}}}
console.log(business1.length)
Wrap your geo.geocode into a Promise
const geoPromise = param => new Promise((resolve, reject) => {
geo.geocode('mapbox.places', param, function(err, geoData) {
if (err) return reject(err);
if (geoData) {
resolve(geoData.features[0])
} else {
reject('No result found');
}
});
});
Combine both calls to geo.geocode
const promises = results.map(result =>
Promise.all([
geoPromise(result.country_name),
geoPromise(result.org_name)
]));
Call them
Promise.all(promises).then(([geoLoc, geoBus]) => {
res.render('layouts/layout', {
results: JSON.stringify(geoLoc),
businesses: JSON.stringify(geoBus)
});
});
As MadWard's answer mentions, deconstructing the argument of the callback of Promise.all is necessary since everything will be in the first argument. Make sure you check out his post for more details
Something important to recall: you will never have more than one argument in a then() callback.
Now you may ask: in the case of Promise.all(), what is this value?
Well, it is an array with all the values from the promises it awaits, in the order in which they are called.
If you do:
Promise.all([
resolveVariable1, resolveVariable2, resolveVariable3
]).then((values) => {
})
values will be [variable1, variable2, variable3], the three variables that the promises resolve to.
Your case is, however, a bit more complicated. What is gonna be returned at the end is a 2-D array containing every entry. It is an array of length results.length, and each of its element has a length of 2. The first element is the result, and the second one is the business.
Here is your snippet:
Promise.all(promises)
.then((values) => {
let results = values.map(elmt => elmt[0]);
let businesses = values.map(elmt => elmt[1]);
res.render('layouts/layout', {
results: JSON.stringify(results),
businesses: JSON.stringify(businesses)
});
})
I just started using Promise in my node app. And I believe that in the code below the loop will break if one of then returns an error, which is rejected.
Is there a way to let the loop finish, skipping the ones with errors. And I still would like all the error notices at the end of the loop.
One more question: Is there a better way to resolve instead of using count++; if(count===items.length) resolve(items)
get_customer_purchase = (items) => {
return new Promise((resolve, reject)=>{
let count = 0;
for (let i in items) {
get_options(items[i].id).then((options)=>{
//do some process
count++; if(count===items.length) resolve (items)
}).catch((error)=>reject(error))
}
})
}
You can write like this:
get_customer_purchase = (items) => {
const promiseArray = items.map((item) => {
return get_options(item.id)
})
return Promise.all(promiseArray)
.then((optionsResult) => items)
}
Notice that if one get_options will fail, you will receive a single fail:
get_customer_purchase(array)
.then(items => /** do stuff */ )
.catch(error => /** one get_options fail */ )
If you want to ignore the errors of some get_options you could simply change:
return get_options(item.id).catch(err => {return null})
and then use a .filter function in optionsResult:
.then((optionsResult) => optionsResult.filter(_ => _!==null))
In my NodeJS application, I am building an API that first fetches all tarrifs name from tarrifs collection then based on all those tarrifs I want to return the counts of these tariffs allocated to users I tried the below-given code
router.get('/getTarrifDetails', (req,res,next) => {
result=[];
tname=[];
counts=[];
Tarrif.find().distinct('tarrif_type', (err,docs) => {
docs.forEach((ele) => {
tname.push(ele);
User.countDocuments({tarrif_type:ele}, (uerr,usr) => {
counts.push(usr);
});
result.push(ele);
});
result.push(counts);
});
});
When I console.log(result) it only shows one array of tarrif_type and other array is empty
You need to understand how the event loop works. I was here once, and I made the same mistakes once.
Try to sync your callbacks since you want to sequentially, like so:
router.get('/getTarrifDetails', (req, res, next) => {
let result = [], count = 0;
Tarrif.find().distinct('tarrif_type', (err, docs) => {
async.forEach(docs, async ele => {
try {
let userCount = await User.countDocuments({ tarrif_type: ele });
result.push(userCount);
} catch (err) {
//your err goes here.
}
})
});
});
I am not sure this will work 100%, but try it out and debug a little bit.
i have array of db like
const dbArr = ["http://localhost:5984", "http://xyz_couchdb.com:5984"]
data to insert
let data ={
_id: 324567,
name: Harry,
gerder: male
}
here is the logic i am using nano module
return new Promise((resolve, reject) => {
let res = [];
let rej = [];
let counter = 0;
for(let i = 0; i < dbArr.length ; i++){
dbArr[i].insert(data, (err, body) => {
err ? rej.push(err) : res.push(body)
if(counter === obj.dbArray.length -1){
rej.length ? reject(rej) : resolve(res)
}
counter++;
})
}
})
what can be the best possible way to achieve this using promise or async module or anything.
In the following example, we gotta use Array.map to create one promise for each element of dbArr, then we gotta wait all promises to end using Promise.all. The catch is here so we handle the errors.
function getAll(dbArr) {
return Promise.all(dbArr.map(x => x.insert(data)));
}
getAll(dbArr)
.then((rets) => {
// Handle the returns
// They are in an array
})
.catch((err) => {
// Handle the error
});
EDIT :
Ok after checking out the documentation of node-couchdb (the one I suppose you use) - I saw that the .insert() method do not return a Promise but only a callback.
So we gotta transform the method, so it will return a Promise using util.Promisify()
const {
promisify,
} = require('util');
function getAll(dbArr) {
return Promise.all(dbArr.map(x => promisify(x.insert)(data)));
}
getAll(dbArr)
.then((rets) => {
// Handle the returns
// They are in an array
})
.catch((err) => {
// Handle the error
});
I'm going nuts with this code. I had tried everything I could think off, and I know is promises-related...but I can't get it working!
The original code is not as simple as the one I'm sharing, but this is te core of the problem:
Lets have two filled arrays, and two empty arrays.
Then 'crearMazo' must run a loop of one of the filled arrays, searching on my mongodb (mongoose) for those strings, and pushing the resulting _id on one of the empty arrays.
Well, It doesn't work. The last console.log show an empty array, even when the console.log inside the loop do print the array.
I know...I'm doing the promises wrong (obviously)...but I can't find where :(
var cartas = ['Lorem', 'Lorem2', 'Lorem3', 'Lorem4', 'Lorem5', 'Lorem6', 'Lorem7', 'Lorem8'];
var cartas2 = ['Lorem', '2Lorem', '3Lorem', '4Lorem', '5Lorem', '6Lorem', '7Lorem', '8Lorem'];
var newMazo = [];
var newMazo2 = [];
let crearMazo = function (c,m) {
return new Promise((resolve, reject) => {
setTimeout(() => {
for(var i in c){
Card.findOne({'nombre': c[i]}, '_id').then( carta => {
m.push(carta._id);
});
}
resolve(m);
}, 0);
});
};
crearMazo(cartas,newMazo)
.then(crearMazo(cartas2,newMazo2))
.then(() => {
console.log('mazo: '+ newMazo);
console.log('mazo: '+ newMazo2);
});
You must pass a callback as the argument to then, not a promise. You could use
crearMazo(cartas,newMazo)
.then(() => crearMazo(cartas2,newMazo2))
.then(() => {
console.log('mazo: '+ newMazo);
console.log('mazo: '+ newMazo);
});
but the proper solution would be to run them in parallel, and use their respective results:
Promise.all([
crearMazo(cartas, []),
crearMazo(cartas2, [])
]).then(([newMazo, newMazo2]) => {
console.log('mazo1: '+ newMazo);
console.log('mazo2: '+ newMazo2);
});
Also you are starting asynchronous actions in a loop here, and creating multiple promises for them - without awaiting any of them. You'll want
function crearMazo(c, m) {
return new Promise((resolve) => setTimeout(resolve, 0)) // is that actually needed?
.then(() => {
var promises = c.map(n =>
Card.findOne({'nombre': n}, '_id').then(carta => carta._id);
);
return Promise.all(promises);
// ^^^^^^^^^^^
}).then(res =>
m.concat(res)
);
}