Pass a function into a Promise ES6 - javascript

I've seen questions similar to this in various forms but I can't seem to crack this particular case...
I want to pass a function inside a Promise, then execute that Promise and do something with the result. The function to be passed in is the database transaction txn function below:
db.transaction(txn => {
//lodash reduce function to execute each promise sequentially
_.reduce(promisesToExecute, (pending, next, i) => {
return next
//this bit is wrong but I don't know how else to pass in txn
//how should I pass txn into the promise?
.then(fn => fn(txn))
.then(newIds => {
if (newIds) {
returnContent[i] = newIds
}
return next
})
}, Promise.resolve())
})
And the promise I want to execute is here
(newVals, id, fkId) => {
return new Promise((resolve, reject) => {
return txn => {
//I want txn to be available in here to use
return db.table('Users')
.insert(newVals)
.transacting(txn)
.then(res => {
resolve(res.id)
})
.catch(err => {
reject(err)
})
}
})
Any ideas? Do I need to somehow pass the newIds => {} function in as a callback?

The problem here is that you're creating promises that will never resolve. They have a function inside them that never gets called, and the resolve and reject hang off of that function.
So fix your second chunk of code to return functions, not promises:
(newVals, id, fkId) =>
txn =>
db.table('Users')
.insert(newVals)
.transacting(txn)
.then(res => res.id)
Then fix the first chunk of code accordingly:
db.transaction(txn =>
_.reduce(functions, (pending, next, i) =>
next(txn)
.then(newIds => {
if (newIds) {
returnContent[i] = newIds
}
})
, Promise.resolve())
);

Related

Returning a value of zero when I'm trying to total a price within a promise

Im trying to pull data from an API(firebase) and Im getting back the correct data but when i try and resolve the final Price in the Promise I keep getting a value of 0. I've tried moving around the original variable to change the scope and nothing has helped so far.
const staffTotalPrices = (eventFirebaseKey) => new Promise((resolve, reject) => {
eventStaff.getEventStaff(eventFirebaseKey).then((staffArray) => {
let staffTotal = 0;
staffArray.forEach((staff) => {
staffData.getSingleStaff(staff.staffUid).then((staffObject) => {
staffTotal += parseInt(staffObject.price, 10);
return staffTotal;
});
});
resolve(staffTotal);
}).catch((error) => reject(error));
});
I've been pushing it to an empty array, then doing a .reduce array method to add the totals, but I'm having to put a timeout when calling it/reducing it just to wait on the API response
In your forEach loop you are calling an async function but not awaiting its result. So you are calling resolve(staffTotal) before any of the staffData.getSingleStaff resolved.
You could for instance do a Promise.all() which will execute all promises and resolve with an array of the results.
const staffTotalPrices = (eventFirebaseKey) => new Promise((resolve, reject) => {
eventStaff.getEventStaff(eventFirebaseKey)
//execute getSingleStaff for all elements in the array and resolve when all are resolved
.then(staffArray => Promise.all(staffArray.map(staff => staffData.getSingleStaff(staff.staffUid))))
//sum up the prices in the staffObjects array with reduce
.then(staffObjects => staffObjects.reduce((a,c) => parseInt(a.price, 10) + parseInt(c.price, 10), 0))
//resolve the promise with the sum
.then(totalStaff => resolve(totalStaff));
.catch((error) => reject(error));
});
Another possibility would be, keeping a count of resolved promises inside the forEach loop. And once, all promises have resolved, also resolve the outer promise. But then of course, you will also need to catch a reject of the inner promises, as otherwise if one of them rejects your promise might stay in pending state.
const staffTotalPrices = (eventFirebaseKey) => new Promise((resolve, reject) => {
eventStaff.getEventStaff(eventFirebaseKey).then((staffArray) => {
let staffTotal = 0; let resolveCounter = 0;
staffArray.forEach((staff) => {
staffData.getSingleStaff(staff.staffUid)
.then((staffObject) => {
staffTotal += parseInt(staffObject.price, 10);
if (++resolveCounter == staffArray.length)
resolve(staffTotal);
})
.catch(e => reject(e));
});
}).catch((error) => reject(error));
});

Why do we use return in promise chain even we already have return in the function?

I'm new to JavaScript. In the following code, may I know why I still have to use return getRecipe(IDs[2]) instead of just call getRecipe(IDs[2]) in the .then method? Even getRecipe() already have return new Promise inside it? I find that I'll get an undefined error if I don't use return in the .then method. Is the return actually return the promise we get to the next then? But why and how? Thank you so much!
const getIDs = new Promise((resolve, reject) => {
setTimeout(() => {
resolve([523, 883, 432, 974]);
}, 1500);
});
const getRecipe = recID => {
return new Promise((resolve, reject) => {
setTimeout(
ID => {
const recipe = { title: 'Fresh tomato pasta', publisher: 'Jonas' };
resolve(`${ID} : ${recipe.title}`);
},
1500,
recID
);
});
};
getIDs
.then(IDs => {
console.log(IDs);
return getRecipe(IDs[2]);
})
.then(recipe => {
console.log(recipe);
})
.catch(error => {
console.log('Error!!');
});
In a chain of .then statements, when you return something from a .then, it goes to the next .then, if there is one. In this case, we are using .then statements to do multiple tasks, and the first task is to get a recipe according to some ID. Once this recipe is received (as a result of the getRecipe function) we return it into the next .then, which has the task of console.log'ing the recipe. If we did not return the getRecipe(ID[2]) result, we would have no 'recipe' parameter of the next .then statement

How to handle an array of Promises then/catch

I don't know to to resolve this situation using JS promises.
Imagine I have some articles and I want to send a patch request to update them. I send one request per article. If the request of one article success then I update that article, but if the request fails, then I update the article differently. Also I want to show a message to the user informing if all the articles have been updated correctly or not.
This is not my real scenario and this may be a weird example. But it's what I want to accomplish in my React app.
Here is what I'm trying to do right now:
const saveArticles = articles => {
const promises = [];
articles.forEach(article => {
const promise = axios
.patch('/articles', article)
.then(() => updateArticleUi(article))
.catch(() => updateArticleUiWithError(article));
promises.push(promise);
});
Promise.all(promises)
.then(() => tellTheUserThereWasNoErrors())
.catch(() => tellTheUserThereWasSomeErrors());
};
This isn't working because the Promise.all is always executing the then callback, weather all promises succeed or not.
Thanks!
Your updateArticleUi and updateArticleUiWithErrors should have the respective return values so that you can distinguish whether there was an error or not when looking at the array of results:
function updateArticleUi(article) {
…
return {success: true};
}
function updateArticleUiWithError(article, error) {
…
return {success: false};
}
function saveArticles(articles) {
const promises = articles.map(article => {
return axios.patch('/articles', article).then(() =>
updateArticleUi(article)
, err =>
updateArticleUiWithError(article, err)
);
});
return Promise.all(promises).then(results =>
if (results.every(res => res.success)) {
tellTheUserThereWasNoErrors();
} else {
tellTheUserThereWasSomeErrors();
}
});
}
This works:
function axiosPatchRequest(url,data) {
return new Promise(function (resolve, reject) {
if (data) {
resolve(url);
} else {
reject('DATA_NOT_FOUND');
}
})
}
function updateArticleUi(data,article) {
return new Promise(function (resolve, reject) {
resolve({type:"updateArticleUi ",data,article});
})
}
function updateArticleUiWithError(data,article) {
return new Promise(function (resolve, reject) {
reject({type:"updateArticleUiWithError ",data,article});
})
}
function tellTheUserThereWasNoErrors(data){
console.log("tellTheUserThereWasNoErrors",data);
}
function tellTheUserThereWasSomeErrors(error){
console.log("tellTheUserThereWasSomeErrors",error);
}
const execute = (articles)=>{
const promises = [];
articles.forEach(article => {
const promise = axiosPatchRequest('/articles', article)
.then((data) => {
return updateArticleUi(data,article);
})
.catch((error) => {
return updateArticleUiWithError(error,article);
});
promises.push(promise);
});
Promise.all(promises)
.then((data) => tellTheUserThereWasNoErrors(data))
.catch((error) => tellTheUserThereWasSomeErrors(error));
};
execute(["one","","three"]);
execute(["one","two","three"]);
Promise.all will always call then because the promise you add to promises is already resolved one with either then or catch attached above
if you want to see catch to happen in .all chain just throw some exeption in updateArticleUiWithErro function
The problem is that you are catching the error thrown by
const promise = axios
.patch('/articles', article)
.then(() => updateArticleUi(article))
.catch(() => updateArticleUiWithError(article));
you should either throw a new error in the axios catch() or let the error bubble up by removing the catch from the axios call.

Promise chain with an asynchronous operation not executing in order

I have found other people asking about this topic but I haven't been able to get my promise chain to execute in order.
Here is a basic reproduction of what is happening:
function firstMethod(){
dbHelper.executeQuery(queryParameters).then(result => {
if (result === whatIAmExpecting) {
return dbHelper.doDbOperation(secondQueryParameters)}
else {
throw new Error('An error occurred')
}})
.then(doFinalOperation())
.catch(error => {
})
}
In the above code doFinalOperation() is called before the then function after executeQuery() is called.
Here is the implementation of executeQuery():
function executeQuery(parameter) {
return new Promise((resolve, reject) => {
const queryToExecute = `SELECT * FROM parameter`
return mySqlConnection.query(queryToExecute).then((result) => {
resolve(result)
}).catch(error => {
reject(error)
})
})
And here is the implementation of of the mySqlConnection.query method:
function query(queryString){
return new Promise((resolve, reject) =>
{
initConnection()
connection.connect()
require('bluebird').promisifyAll(connection)
return connection.queryAsync(queryString).then(function(results) {
connection.end();
resolve(results)
}).catch(error => {
reject(error)
})
})
It seems like I have incorrectly implemented the executeQuery() method. The database operation in the mySqlConnection.query is asychronous and I can see that's where the chain of promises stops happening in the expected order.
My question in a nutshell: How do I make the my chain of promises execute in order, and how I do stop a then() method from being executed before the previous Promise has called resolve() or reject()?
Thanks in advance.
then expects a function, but you have accidentally executed it, instead of passing it. Change:
then(doFinalOperation())
with:
then(doFinalOperation)
Now it will be the promise implementation that invokes it (at the proper time), not "you".
If your function needs arguments to be passed, then you can either
(1) Use bind:
then(doFinalOperation.bind(null, parameterOne, parameterTwo, parameterThree))
(2) Use a function expression
then(_ => doFinalOperation(parameterOne, parameterTwo, parameterThree))
Both your .then() methods get called on the first async operation...
Should be something like this:
function firstMethod(){
dbHelper.executeQuery(queryParameters).then(expectedResult => {
if (expectedResult === whatIAmExpecting) {
return dbHelper.doDbOperation(secondQueryParameters)}
.then(doFinalOperation())
.catch(error => {
};
}
else {
throw new Error('An error occurred')
}})
.catch(error => {
});
}

Plain es6 promise; how to do n arbitrary operations?

I have two simple methods
// send a single command
sendCommand(command) {
return new Promise((resolve, reject) => {
this._commands.write(command, (response) => {
response === '?' : reject(command) : resolve(command);
});
});
}
// has to send multiple commands and wait for the result
sendArray(name, array) {
let bits = _.chunk(array, 4);
_.each(bits, (bit, index) => {
this.sendCommand(`QD ${name}[]${bits.join('\r')}\\`);
});
}
However, is there any way for this array be sent through the promises iteratively with plain es6 promises? Eg:
// for every bit in bits
this.sendCommand(bit1)
.then(() => { this.sendCommand(bit2) })
// ...
.then(() => { this.sendCommand(bitN) })
.catch(console.log);
Something like
let allBitsPromise = _.chunk(array, 4).reduce(function (p, bit) {
return p.then(() => sendCommand(bit));
}, Promise.resolve());
would work.
The least obvious part of this (to me, anyway) is that if the callback passed to then returns a promise p then the original promise is resolved when p is. So a promise like
Promise.resolve().then(() => {
return new Promise((resolve, reject) => setTimeout(resolve, 2000))
});
Is only resolved when the promise returned by the callback is resolved by setTimeout.
Keep a reference to the promise returned by the function and chain the .then call on it:
let promise = Promise.resolve();
_.each(bits, (bit, index) => {
promise = promise.then(() => this.sendCommand(`QD ${name}[]${bits.join('\r')}\\`));
});
promise.catch(error => console.log(error));
Note that this will send the data sequentially, which I assume is what you want.

Categories

Resources