Sequelize unable to assign data after query with .then() - javascript

I have this function but I am unable to put it inside my item variable. How do I put it outside the item variable?
function getItems(id) {
let item;
sequelize.query("SELECT * FROM table WHERE id = " + id, {type: Sequelize.QueryTypes.SELECT})
.then(myTableRows => {
// item = JSON.stringify(myTableRows);
});
return item;
}

Your sequelize query returns a promise, and you cannot set the value of the item inside the promise and expect an immediate response. If you want it to work this way, you need to handle the promise correctly, or use an async-await. You can reconfigure your function to look like this, which will handle the issue:
async function getItems(id) {
let item;
item = await sequelize.query("SELECT * FROM table WHERE id = " + id, {type: Sequelize.QueryTypes.SELECT});
return item;
}
===========================================================================
Update:
I am not sure your experience level with node, but many aspects of node leverage promises. At a high level, what that means is perform some action, then do something once its done. With sequelize, you are performing a SQL query, and once you get a response, you can now do something with this data.
Async-Await is a way to make promises a bit more straightforward in understanding, but act the same. There are plenty of articles online to help you better understand the concepts.
Now the function that is calling getItems needs to also be an async-await, or call it as a promise in order to actually get the values. For example:
async function test(id) {
let items = await getItems(id);
console.log(items)
}
Hopefully that clears up some confusion, though you many need to learn a bit more about promises if you are unclear about how they work. Hopefully this helps.

Related

How to await asynchronous map function?

let me quickly get to the point. I have a function which fetches some user ids. It works very well.
const allIds = await fetchAllIds(someUrl);
Now the problem comes when I want to do something different. I'd like to loop over those ids and do some async await stuff with them. That is, to mainly fetch some data for each one with axios and modify them accordingly.
allIds.map(async (id) => {
// 1. Fetch some data (try, catch and await)
// 2. Modify the id based on that data
// 3. Return the id, namely replace the old one
});
At the end of my code, I simply return allIds. The problem is that it returns them without waiting for the map function to execute completely. I tried different methods and none of it seems to be working. Could you please help me to make it work or perhaps suggest some other possible solutions? Thanks in advance!
You basically have two problems:
You are ignoring the return value of the map
The map will return an array of Promises and you aren't awaiting them all
So:
const promises = allIds.map(...);
const replacement_ids = await Promise.all(promises);
return replacement_ids;
Use this instead.
const newList = await Promise.all(allIds.map(id=>new Promise(async(res)=>{
// do your async here and get result;
res(result);
})));

How to optimize this block of code since its async

var orderItems = userData.shoppingcart;
var totalPrice = 0;
userData.shoppingcart.forEach(function(itemName, i){
_data.read('menuitems', itemName, function(err, itemData){
if(!err && itemData)
{
totalPrice += itemData.price;
if(++i == userData.shoppingcart.length){
// Only here when all the itemNames have been read I should continue
}
}
});
});
As you can see, the call to _data.read is async, because I am reading from a file.
But I need to wait to have all the files read, so I can have the totalPrice calculated. That's why I place that condition [ ++i == userData.shoppingcart.length ].
I am new to javascript and nodejs in general, never was very good programmar at it, but anyway my point is that I am sure this isn't a good approach, what if both files are readed at same time, and then that condition is never ran or the calculation of totalPrice is badly done?
Can someone give me please some guidance on this?
Thank you in advance!
Given that you don't specify what context this is in, I'm going to make a few assumptions:
I assume that _data.read() doesn't already support returning a promise.
I assume that this code needs to either call a callback function or return a promise.
My (somewhat naive) approach to this would be to:
Map the orderItems into Promises for each price of that item.
Map the result into a total
Return that resulting promise OR call the callback.
Here's an annotated example of how you might do it:
// Promise.all takes an array of promises and returns
// a new promise that completes when all the promises in the array are complete.
const promiseOfPrices = Promise.all(
// Here we map all the items in the shopping cart into promises for each of their prices
userData.shoppingcart.map(
// The Promise object takes a single function that it will immediatly call with functions to resolve or
// reject the promise. I suggest reading up on Promises if you're not familiar with them.
itemName => new Promise((resolve, reject) => {
// Here, we have a `reject` and `resolve` function that will each complete the new promise,
// either in success or error respectfully.
// Do the actual read of your file or database or whatever
_data.read('menuitems', itemName, (err, itemData) => {
// If there was an error, reject this promise.
if (err) reject(err);
// Otherwise, we're successful and we resolve with the price of the item
else resolve(itemData.price);
});
})
)
);
// Now, we have a promise (promiseOfPrices) for all the prices of the items in the cart. We use `then` which will
// perform a transform on the result, much like the `map` function on an Array.
const promiseOfTotal = promiseOfPrices.then(
// Here we use the `Array.reduce` function to succinctly sum the values in the array.
arrayOfCartItemPrices => arrayOfCartItemPrices.reduce(
// For each item, reduce calls our function with the current sum and an item in the array. We produce a new
// sum by adding the sum to the item price.
(sum, itemPrice) => sum + itemPrice,
// This is the initial value for sum, 0.
0
)
);
If you can return a promise, and you just want to return the total, then
return promiseOfTotal;
If you have a callback that expects (err, result), then do something like this:
promiseOfTotal.then(
result => callback(null, result),
error => callback(error, null),
)
If you need to do more work on the result, you can do so with another then:
promiseOfTotal.then(
priceSum => {
// Do work here
},
// Optionally handle errors here:
error => {
// Do error handling here.
}
)
Note that by using promises, arrow functions, and array comprehensions (map and reduce) we avoid complex and hard to follow mutation of variables and loops. This is a "functional" style of programming, and while somewhat harder to learn, is generally safer and cleaner than the alternatives. I suggest taking the time to understand how this works, as it'll help you write code that's less likely to have bugs when you're dealing with complex things like asynchronicity.
Finally, I haven't run this code. It may well have a bug or two. Feel free to ask for clarification or if it doesn't work.
Best of luck!
P.S. I didn't go in to using async/await because I think it would be less clear than using the Promises directly, and the use of Promise.all is needed for the parallelism anyways. It's absolutely possible to use them to good effect here, but I'll leave that as an exercise to the OP.
This is how you can read the items in sequence using promises (async/await flavour):
var orderItems = userData.shoppingcart;
let totalPrice = 0;
for (let itemName of userData.shoppingcart) {
const itemData = await _data.read('menuitems', itemName);
totalPrice += itemData.price;
}
This example assumes that _data.read supports async/await. If it doesn't, however, it can "promisified" using the promisify function in nodejs' util module

Cleanest way to use async results in Javascript

Coming from C++ and Python, I still struggle with Javascript asynchronous ubiquity. Sometimes it's very useful, but other times I just don't see how to fit it in without writing terrible code.
I have a Node.js + Express CRUD setup, and I have to do some basic check-ups before continuing with the request. I want to check that http POST fields match with database fields before running the final query. I cannot declare it an async function and use await as it must match a given Interface.
showColumns(dbTable) returns a db premise with a SHOW COLUMNS query.
The only solution that I found is:
database.showColumns(dbTable).then((columns)=>{
//But do I really need to put all my logic inside a then???
let row = Object.keys(req.body).filter({}.hasOwnProperty.bind(columns));
//... all the rest of the logic goes here
});
In your opinion, what is the cleanest/most elegant way to solve that?
database.showColumns(dbTable)
.then(columns => this.handleColumns(columns))
.then(parsedData => this.doSthElse(parsedData);
You can extract your logic to a seperate method. However, it must be called inside then as it is the callback triggered once your async operation is finished.
Alternatively, you might consider using generators, async/await functions or promises.
You can use async/await for this.
(async function() {
try {
var columns = await database.showColumns(dbTable)
let row = Object.keys(req.body).filter({}.hasOwnProperty.bind(columns));
} catch (e) {
console.log("Promise error");
}
})

Knex.js non-terminating functions

I don't have any problem in this question, I am just interested in how knex.js menaged to something.
In code, you can write something like this
let search = knex.table('users').select('something')
if(params.minprice) search.where('minprice', params.minprice)
if(something) search.something()
let result = await search
It works, but I don't get how did they menage to hold query execution until await occured? If we did await, it means function was async aka returned a promise. But in javascript, promise executes as soon as function that returned it is called, it does not care is there .then() or .catch(). Should not query execution start al line 1? What is more, when I log search, it is not a promise, but some kind of object, so how can it be awaited?
Can someone provide a simple example how to achieve such a behaviour?
I'm guessing that search contains a property named then, which is a function that initiates the search and also behaves similarly to the functionality of Promise.prototype.then.
E.g.:
// define Searchable
let Searchable = function() {
this.searchParam = 'param';
};
Searchable.prototype = {
setSearchParam: function(p) { this.searchParam = p; },
initiateSearch: async function() {
// lots of fancy searching
console.log(`Searching with param "${this.searchParam}"`);
return `search based on "${this.searchParam}"`;
},
then: async function(callback) {
// initiate the search:
let searchResults = await this.initiateSearch();
// behave kind of like `Promise.prototype.then`!
return callback(searchResults);
}
};
// now work with it:
(async () => {
let searchable = new Searchable();
searchable.setSearchParam('mySearchParam');
console.log('No search performed yet!');
// Here's the fancy usage you're concerned with (it invokes `searchable.then`):
let searchResult = await searchable;
console.log('RESULT:', searchResult);
})();
Calling await on some value will attempt to call value.then as if it were a function accepting a callback argument.
Knex query builder is mutable and thenable object.
So every time you call for example search.where(...) for that query builder, its internal state changes and stores that new where clause.
Query builder being thenable means that the object has .then() method and when you call await search it is actually pretty much equivalent with await Promise.resolve(search) which first executes thenable and converts it to promise which is then resolved or an exception might occur.
Thenable objects are actually pretty important part of promise spec providing interoperability API between promises and non-promise objects.

Chaining Promises recursively

I'm working on a simple Windows 8 app in which I need to fetch a set of data from a web site. I am using WinJS.xhr() to retrieve this data, which returns a Promise. I then pass a callback into this Promise's .then() method, which supplies my callback with the returned value from the asynchronous call. The .then() method returns another Promise, giving it the value that my callback returns. The basic structure of such a query would be as follows:
WinJS.xhr({ url: "http://www.example.com/" }).then(
function callback( result_from_xhr )
{
//do stuff
return some_value;
}).then(
function secondcallback( some_value )
{
//do stuff
});
In my situation, however, I may need to make additional queries for data depending on the data returned by the first query, and possibly more queries depending on THAT data... and so on, recursively.
I need a way to code this such that the final .then() is not executed until ALL the recursions have completed, similar to this:
function recurse() {
return WinJS.xhr({ url: "http://www.example.com/" }).then(
function callback( result_from_xhr )
{
if( result_from_xhr == something )
{
recurse();
}
});
}
recurse().then(
function final()
{
//finishing code
});
The problem is that, of course, the finishing code is called as soon as the first level of recursion completes. I need some way to nest the new promise and the old promise from within the callback.
I hope my question is clear enough, I'm really not sure how to explain it and frankly the idea of asynchronous recursive code makes my head hurt.
What I would do here is to create a whole new, standalone promise that you can complete manually, and return that from the recurse() function. Then, when you hit the point that you know you're done doing async work, complete that promise.
Promise.join works when you've got a known set of promises - you need the entire array of promises available before you call join. If I followed the original question, you have a variable number of promises, with more possibly popping up as part of async work. Join isn't the right tool in these circumstances.
So, what does this look like? Something like this:
function doSomethingAsync() {
return new WinJS.Promise(function (resolve, reject) {
function recurse() {
WinJS.xhr({ url: "http://www.example.com/" })
.then(function onResult(result_from_xhr) {
if (result_from_xhr === something) {
recurse();
} else {
// Done with processing, trigger the final promise
resolve(whateverValue);
},
function onError(err) {
// Fail everything if one of the requests fails, may not be
// the right thing depending on your requirements
reject(err);
});
}
// Kick off the async work
recurse();
});
}
doSomethingAsync().then(
function final()
{
//finishing code
});
I rearranged where the recursion is happening so that we aren't recreating a new promise every time, thus the nested recurse() function instead of having it at the outer level.
I solved this problem in perhaps a different way (I think it's the same problem) while making my own Windows 8 app.
The reason I came across this problem is because, by default, a query to a table will paginate, and only return 50 results at a time, so I created a pattern to get the first 50, and then the next 50, etc, until a response comes back with less than 50 results, and then resolve the promise.
This code isn't going to be real code, just for illustration:
function getAllRows() {
return new WinJS.Promise(function(resolve, reject){
var rows = [];
var recursivelyGetRows = function(skipRows) {
table.skip(skipRows).read()
.then(function(results){
rows = rows.concat(results);
if (results.length < 50) {
resolve(rows);
} else {
recursivelyGetRows(skipRows + 50);
}
})
}
recursivelyGetRows(0);
});
}
so getAllRows() returns a promise that resolves only after we get a result back with less than 50 results (which indicates it's the last page).
Depending on your use case, you'll probably want to throw an error handler in there too.
In case it's unclear, 'table' is a Mobile Services table.
You will need to use Promise.join().done() pattern. Pass an array of Promises to join(), which in your case would be a collection of xhr calls. The join() will only call done() when all of the xhr Promises have completed. You will get an array of results passed to the done(), which you can then iterate over and start again with a new Promise.join().done() call. The thing to be away of, when using this approach, is that if one of the Promises passed to join() fail, the entire operation is treated as an error condition.
Sorry I don't have time right now to try and stub out the code for you. If I get a chance, I will try to later. But you should be able to insert this into your recursive function and get things to work.
Well, I solved my problem; my recursive function was misinterpreting the data and thus never stopped recursing. Thank you for your help, and I'll be sure to watch those screencasts, as I still don't quite fully grasp the Promise chaining structure.

Categories