I am trying to verify if a object already exists in a wix collection and if it does cancel the inset() call to the database
import wixData from "wix-data";
export function Memberships_beforeInsert(item, context) {
var name = item.firstName + item.lastName;
name = name.toLowerCase();
wixData.query(context.collectionName)
.find()
.then((res) => {
var members = res.items;
var len = res.length;
console.log(len)
for (var i = 0; i < len; i++) {
let member = members[i];
let memberName = member.firstName + member.lastName;
memberName = memberName.toLowerCase();
if (memberName === name) {
let toUpdate = {
'_id': member._id
}
wixData.update(context.collectionName, toUpdate)
return null;
}
return item;
}
});
//toHere
}
Im fairly new to and wixCode but I was expecting this to wait until the .then() is called then return as follows, but due to wixCode utilizing promises, the code goes immediately to the //toHere section of the code in which it dosent find a return and dismisses the call. Which adds the data to the database instead of returning null.
OK so when we look at your code it seems that you want to find a matching record to the one you are trying to insert to Memberships and then abort the insert and execute an update if one exists. The better way to do this is to look for the specific record match using the query .eq() function. If this finds a matching record then you can update otherwise continue with the insert. See below.
So quickly what is a promise?
In layman's terms think of a Promise like a FedEx tracking code.
When you call a promise based function to ask it to do something you are given back a Promise that what you asked for will be done or that you will be told if a problem occurred.
Just like a fed ex tracking code - you don't know if something has arrived (your function has done what you want it to) unless you check the tracking status using the code OR the fedex delivery van arrives and you receive the package. At which point the tracking code is updated to say the package was delivered and you might get a text or email saying it has been delivered.
A Promise function either completes successfully and you get a result in the then function call OR if an error occurs a catch() event is triggered. So All Promise based functions are similar to if then else conditional test only they look like this:
sendFedEx(package) // Call doesn't resolve immediately you have to wait for fedEx!
.then((fedExDeliveryInfo) => {
//Your package arrived!
})
.catch((fedExLostPackageInfo) => {
//Your package got lost :-(
});
In your code you do a case insensitive compare outside of the data collection. This is probably going to get resource intensive with a large data collection. A better way would be to store the case insensitive string:
(item.firstName + item.lastName).toLowerCase() in the data record and then use it for your query using .eq. That way you let the data collection do its job and simplify your code.
Note: This makes use of the fact that beforeInsert() returns a Promise.
Syntax:
function beforeInsert(item: Object, context: HookContext): Promise
Here is a modified suggestion for you with lots of comments!
import wixData from "wix-data";
export function Memberships_beforeInsert(item, context) {
// Memberships_beforeInsert returns a promise. So does
// wixData.query...find() so we simply return it to maintain the Promise
// chain.
var compareName = (item.firstName + item.lastName).toLowerCase();
// Add a compareName column value to item for future querying
// This will be inserted into the data collection
item.compareName = compareName;
//-------------------------------------------------------//
// This is the head of the Promise chain we need to return
//-------------------------------------------------------//
return wixData.query(context.collectionName)
.eq('compareName', item.compareName) // Query the compareName
.find()
.then((res) => {
var members = res.items;
var len = res.length;
console.log(len);
// Should only have one record or no records otherwise we have a
// problem with this code :-)
// So if we have too many we throw and error. This will be caught in
// an outer catch if we have one
if (len > 1) {
throw Error(`Internal Error! Too many records for ${item.firstName} ${item.lastName}`);
}
// If we get here we have a valid record OR we need to return a result
// To do this we will use a return variable set to the item which
// assumes we will insert item. This will be overridden with the save
// Promise if we have a record already
var result = item;
if (len === 1) {
// We have a record already so we need to update it and return null
// to the caller of beforeInsert to halt the insert. This is a
// Simple case of adding the _id of the found record to the item we
// have been given.
item['_id'] = member._id;
// Again remember we are using promises so we need to return the
// wixData.update result which is a promise.
result = wixData.update(context.collectionName, toUpdate)
.then((savedRecord) => {
// Now we have chained the update to the beforeInsert response
// This is where we can tell the Insert function to abort by
// returning null.
return null;
});
}
// Now we can return the result we have determined to the caller
return result;
});
}
This should do what you are trying to accomplish.
Related
I have a javascript application running on NodeJS. For context I am using the express framework as this is meant to be our backend. I have a chunk of code which is meant to get data from a database, filter it and then send it back to the client. Instead, the filtering happens AFTER the response is sent, meaning the client is getting incorrect data. The code is below.
let resultArray = [];
const bulkSearchPromise = new Promise((resolve, reject) => {
for (let index = 0; index < input.length; index++) {
collectionPool.query('SELECT * FROM users WHERE ' + type + ' = $1', [input[index]], (err2, result2) => { // Make a query for each input user is trying to search for
if (err2) console.log("Error in bulk search: " + err2);
else {
if (result2.rows.length > 0) { // If input user searched for was found
pool.query('UPDATE users SET usedsearches = usedsearches + 1 WHERE id = $1', [result.rows[0].id]); // Increments used searches
// The code below will filter useless key value pairs. For example if username: null then there is not a reason to send it back to the client
let filteredArray = [];
for (let index = 0; index < result2.rows.length; index++) {
let array = Object.entries(result2.rows[index]);
let filtered = array.filter(([key, value]) => value != null);
let filteredObject = Object.fromEntries(filtered);
filteredArray.push(filteredObject);
resultArray.push(filteredObject);
}
console.log("a"); // This should run first.
}
}
});
}
resolve("ok");
})
bulkSearchPromise.then((value) => {
console.log("b"); // This should run second
return res.json({
status: 'success',
content: resultArray
}); // resultArray should be populated after the filtering above. Instead it is empty.
})
When the endpoint is hit the output will always be
username
b
a
What I need is for the for loop to run first and then after resultArray is populated, return it back to the client.
I've tried wrapping this code into a promise, but that hasnt helped either as 'resolve("ok")' is still called before the for loop completes.
Your promise is resolving before collectionPool.query is done.
The for loop only runs after collectionPool.query is done but you are resolving the promise before it.
Note that collectionPool.query is async and the its callback will only run when the web-api is finished (if this concept is murky check this out http://latentflip.com/loupe)
Option 1:
Move resolve() inside the collectionPool.query (where the console.log("a");) call back and call resolve(filteredObject).
In addition you should reject(err2) when err2 is not null (not just console)
Option 2:
you can use Util.Promisify to transform collectionPool.query to promise base API which will save you the hustle of manually transform
const util = require('util');
util.promisify(collectionPool.query)(QUERY)
.then(queryRes => {
/* logic to refine queryRes to object */
return filteredObject;
})
in both options you can omit the resultArray from your code. if you resolve(filteredObject) or return filteredObject; in then() you will be able to access this data on the next then (the value in bulkSearchPromise.then((value)).
So I'm making a function for every time I need to work with my database SQLite I give to this function 3 parameters and then it returns for me the result.
The problem is the function is returning a promise, and also I tried to update the value of some variables and they don't change:
Let me show you my code, and then to explain you better about what I'm having trouble with:
//Down here I'm using the function I made, I'm passing as parameters my
// connection, my SQL string, and my arguments
(Main.js)
const resultSQL = sqlExecute(db,'SELECT * FROM tbl_diarys',[]);
console.log(resultSQL)
----- // Now this down here is the function I made------------------------------------------------------
const { Alert } = require("react-native")
const sqlExecute = async (dbConection,sqlQuery,arg,)=> {
let resultSQL = []
let test = 'x' // variable that i created to make a test changing it's value
await dbConection.transaction((tx)=>{
tx.executeSql(sqlQuery,arg,(tx,results)=>
{
Alert.alert('', 'SQL executed with success')
var len = results.rows.length;
var vectorResults = []
for (let i=0; i<len; i++){
let row = results.rows.item(i);
vectorResults.push(row)
}
console.log(vectorResults) // Showing the results from my database
test = 'y' // trying to change the value of variable "test" from 'x' to 'y'
resultSQL = [...vectorResults]; // cloning my results from database to my variable resultSQL
},(error)=>{Alert.alert('Some error had happened'); console.log(error)})
})
console.log(test) // showing the value of my variable "test"
console.log(resultSQL) // showing the value of my variable resultSQL
return (resultSQL);
}
module.exports=sqlExecute
THIS IS MY CONSOLE.LOG
this is a picture of my console.log <----------------------------------
So my questions are:
1º Why my variable "test" never changed it's value to 'y'?
2º Why my function is returning a promise? since I put to return the variable resultSQL?
3º Why my variable resultSQL also never changed it's value to my database results?
As you guys can see my connection with the database is working well returning a vector with results of my SQL query, but I can't manage to return this vector, instead my function is returning a promise.
Thank you guys for helping me, I wish you all a great day!
You have to look into how async/await works. In the background they use promises, so that anwers your second question. That's why your method returns a promise, because it's an async method.
To answer your other 2 questions. It seems like the tx.executeSql() and dbConnection.transaction() methods work with callbacks, but you don't wait for the callbacks to be called and resume executing your code. This is why you think that your variable test and resultSQL aren't changing. They do actually change, but you're calling the console.log before they change.
I would recommend changing your sqlExecute method from async to a promise (I think it's easier to understand for beginners). It would become something like this:
const sqlExecute = (dbConection,sqlQuery,arg,)=> {
return new Promise((resolve, reject)=>{
let resultSQL = []
let test = 'x' // variable that i created to make a test changing it's value
dbConection.transaction((tx)=>{
tx.executeSql(sqlQuery,arg,(tx,results)=>
{
Alert.alert('', 'SQL executed with success');
var len = results.rows.length;
var vectorResults = []
for (let i=0; i<len; i++){
let row = results.rows.item(i);
vectorResults.push(row)
}
console.log(vectorResults) // Showing the results from my database
test = 'y' // trying to change the value of variable "test" from 'x' to 'y'
resultSQL = [...vectorResults]; // cloning my results from database to my variable resultSQL
console.log(test) // showing the value of my variable "test"
console.log(resultSQL) // showing the value of my variable resultSQL
resolve(resultSQL);
},(error)=>{
reject(error);
});
});
});
}
// Call sqlExecute as a promise, for example
sqlExecute(connection, query, arg).then(()=>{
console.log('We did it!');
}).catch((error)=>{
Alert.alert('Some error had happened');
console.log(error);
});
As I said, your code could become "something like this". I give no guarantees this exact code works and you'll have to clean it up a little.
I am looking for a best practice solution for my current ES6 javascript solution. I hope someone could point me in the right direction.
I am at the point, where I am fetching a bunch of posts from a paginated REST API and storing them inside an array. The fetching of posts is async and will finish some time after the DOM is ready.
Once the fetch script is complete, it will spit out an array containing the entire list of posts fetched. When the array is available, I want to loop through it.
I have seen in similar cases, that others used a timer to check each milisecond, if the array is ready. I am not sure, if that it is the correct method to implement.
What is the best practice using ES6 javascript to check if the array from the async function is ready to loop through?
The following script is what I use to fetch the posts with. I have tried to explain the code within the comments.
// Constant variable with the assigned value of a joined string containing the base URL structure of the REST API request.
const apiUrl = websiteUrl + '/wp-json/wp/v2/punkt/';
// Logging out the base URL of the REST API.
console.log('REST API is this: ' + apiUrl);
// Local variable with the default value of true assigned. Variable is used to control the paginated fetch of the API.
let keepfetchingPosts = true;
// Local variable that contains the limit of posts per request.
let limitPerPage = 20;
// Constant variable, which is assigned a function as the value. The function is async and will return a promise.
// The function takes one argument. The argument holds the value of the page number.
const getPosts = async function(pageNo = 1) {
// Local variable assigned with the base URL string of the REST API request. Additional queries are added to the end of the string.
let requestUrl = apiUrl + `?page=${pageNo}&per_page=${limitPerPage}`;
// Logging out the REST API request
console.log('URL is this: ' + requestUrl);
// Logging out the argument 'pageNo'
console.log('Retreiving data from API for page : ' + pageNo);
// Local variable assigned with a fetch function that returns a promise. The URL request are used as the function argument.
let apiResults = await fetch(requestUrl)
// If request is success, then log and return the following to the local variable.
.then(function(response){
// Logging out the status code of the response.
console.log('HTTP response is: ' + response.status);
// return JSON and status code of the XHR request
return {
data: response.json(),
status: response.status
}
})
// Catch the error and log it out within the console.
.catch(function(error){
console.log('HTTP response is: ' + error.status)
});
// If the length of the request is less than the limitPerPage variable and status code is 200, then...
if (apiResults.length < limitPerPage && apiResults.status === 200){
// Set the boolean to false
keepfetchingPosts = false;
// Return the JSON of the successfull response.
return apiResults.data;
} else if (apiResults.status === 200) {
// If the status code is 200, then return the JSON of the successfull response
return apiResults.data;
} else {
// Otherwise, set the boolean to false
keepfetchingPosts = false;
}
}
// Defining a constant variable that holds and async fynction. An async functon will always return a promise.
// The function takes one argument, which is set to 1 by default.
const getEntirePostList = async function(pageNo = 1) {
// Try and catch statement used to handle the errors that might occur.
try {
// Constant variable which is set to the return variable of the function getPost(). Get post returns the successfull paginated response of the request.
const results = await getPosts(pageNo);
// Logging out a string including the length of the array.
console.log('Current array contain ' + results.length + ' items...');
// Conditional statement that checks if the length of the array named 'results' is less than the variable named limitPerPage. Condition is also checked, if bolean is true.
// If the conditions are met, the code will join the arrays into one big array.
if (results.length < limitPerPage && keepfetchingPosts === true) {
// Logging out a string that indicates an attempt to combine that last array to the existing array.
console.log('Combining last array!');
// Return the combined array.
return results;
} else if (keepfetchingPosts === true) {
// Logging out a string that indicates an attempt to combine the recent fetched array to the existing array.
console.log('Combining arrays!');
// Returning the new combined array and increments the pageNo variable with 1.
return results.concat(await getEntirePostList(pageNo+1));
} else {
// Logging out a string that indicates the script will stop fetching more posts from the REST API.
console.log('Stop fetching posts and return results');
// Returning the complete array.
return results;
}
// Catch statement that takes the argument of the error that occured.
} catch(error) {
// Logging out the error.
console.log(error);
}
};( async () => {
// Constant variable with the assigned value received from the function
const entireList = await getEntirePostList();
// Logging out the enite list of results collected from the REST API
console.log(entireList);
})
();
The script spits out the array named 'entireList'.
I'm new to javascript promises and am having difficulty using them with a collection of elements. Within the collection, I perform an operation which returns a promise. Once the entire operation (including all the Promises in the collection) has completed, I need to perform another set of operations. The promises within the collection need to go in sequence.
I have tried the following approach:
public cleanup(onCleanupComplete: any): void {
if (this._app == null) return; //this._app comes out of an external API
// Below line of code won't compile, it is just for illustration.
// I'm trying to show that I need a promise in return from method
this.removeConference(0).then(() => {
// Do additional clean up operation and call onCleanupComplete
onCleanupComplete(true, null);
});
}
private removeConference(i : number) {
if (this._app.conversationsManager.conversations == null
|| i === this.conversationLength)
return; // this.conversationLength equals initial
// number of elements in collection
// How do I return a promise here?
var conversation = this._app.conversationsManager.conversations(0);
console.log("app.cleanup.leave", `Leaving conversation ${conversation}`);
conversation.leave().then(() => {
console.log("app.cleanup.leave", `Conversation ${conversation} left successfully`);
this.app.conversationsManager.conversations.remove(conversation);
_ this.removeConference(i);
});
}
What should I return from removeConference once all the conversations in collection are removed?
So this is something that nabbed me early on in understanding promises. You need to get ALL of your code away from the practice of passing in a callback, and then simply using the promise to call that. Instead, to keep the continuous nature of promises, you want to just return promises to the calling function, unless your function is the one that should decide what to do afterward. So, your code should look something like this.
public cleanup(onCleanupComplete: any):Promise<any> {
if (this._app == null) return; //this._app comes out of an external API
// Below line of code won't compile, it is just for illustration.
// I'm trying to show that I need a promise in return from method
var promiseArray = [];
for (var i = 0; i < this.conversationLength; i++) {
promiseArray.push(removeConference(i));
}
return Promise.all(promiseArray);
}
private removeConference(i : number):Promise<any> {
if (this._app.conversationsManager.conversations == null
|| i === this.conversationLength)
return Promise.resolve(true);
var conversation = this._app.conversationsManager.conversations(0);
console.log("app.cleanup.leave", `Leaving conversation ${conversation}`);
return conversation.leave().then(() => {
console.log("app.cleanup.leave", `Conversation ${conversation} left successfully`);
this.app.conversationsManager.conversations.remove(conversation);
this.removeConference(i);
});
}
I'm not 100% sure this compiles correctly, but hopefully it helps conceptually. Promise.all is really the key function here - it takes an array of promises, and creates a matching "control promise" that only resolves when all of them have.
I making multiple mongoDB queries in loop. and want to send the all results as one data array.But when I simple use the return for send the data it simply return undefined and do not wait for results of all DB request. I also tried to use q.moulde but same issue.
Code:
var getPrayerInCat = function(data){
var result ;
var finalData = [];
if(data.length >0){
data.forEach(function(data2){
var id= data2.id;
Prayer.find({prayerCat:id},function(err,prayer){
var deferred = Q.defer()
if (err) { // ...
console.log('An error has occurred');
// res.send(err);
result= finalData = err
} else {
if(!prayer){
// console.log(data2.id+'--0');
data2.prayersCount = 0;
result = deferred.resolve(finalData.push(data2))
} else {
// console.log(data2.id+'--'+prayer.length);
data2.prayersCount = prayer.length;
// console.log(prayer)
result = deferred.resolve(finalData.push(data2))
} // else for data forward
}
deferred.promise;
})
// deferred.resolve(finalData);
})
/*if(finalData.length > 0) { return finalData;}*/
}
}
finalData is returned undefined.
Let's start with the general rule for using promises:
Every function that does something asynchronous must return a promise
Which functions are these in your case? It's getPrayerInCat, the forEach callback, and Prayer.find.
Hm, Prayer.find doesn't return a promise, and it's a library function so we cannot modify it. Rule 2 comes into play:
Create an immediate wrapper for every function that doesn't
In our case that's easy with Q's node-interfacing helpers:
var find = Q.nbind(Prayer.find, Prayer);
Now we have only promises around, and do no more need any deferreds. Third rule comes into play:
Everything that does something with an async result goes into a .then callback
…and returns the result. Hell, that result can even be a promise if "something" was asynchronous! With this, we can write the complete callback function:
function getPrayerCount(data2) {
var id = data2.id;
return find({prayerCat:id})
// ^^^^^^ Rule 1
.then(function(prayer) {
// ^^^^^ Rule 3
if (!prayer)
data2.prayersCount = 0;
else
data2.prayersCount = prayer.length;
return data2;
// ^^^^^^ Rule 3b
});
}
Now, we have something a bit more complicated: a loop. Repeatedly calling getPrayerCount() will get us multiple promises, whose asynchronous tasks run in parallel and resolve in unknown order. We want to wait for all of them - i.e. get a promise that resolves with all results when each of the tasks has finished.
For such complicated tasks, don't try to come up with your own solution:
Check the API of your library
And there we find Q.all, which does exactly this. Writing getPrayerInCat is a breeze now:
function getPrayerInCat(data) {
var promises = data.map(getPrayerCount); // don't use forEach, we get something back
return Q.all(promises);
// ^^^^^^ Rule 1
}
If we needed to do anything with the array that Q.all resolves to, just apply Rule 3.