I want to show the current user his information which are stored in my DB. It is working fine. The only problem is to wait for the data to be loaded. In my code below I am using the await keyword to wait for my data to load. But however the onReady function will be triggered twice and also my json object does not include the keyword, but it definitely does.
import wixData from "wix-data";
import wixUsers from "wix-users";
async function getTeamData() {
let user = wixUsers.currentUser;
let user_id = user.id;
let options = {
suppressAuth: true,
suppressHooks: true,
};
wixData
.get("Members/PrivateMembersData", user_id, options)
.then((results) => {
let item = results;
let email = item.loginEmail;
let data;
console.log("Compare email: ", email);
wixData
.query("Users")
.eq("Owner", email)
.find()
.then((found) => {
if (found.items.length > 0) {
data = found.items;
console.log(data);
return data;
} else {
console.log("No matching data found!");
return null;
}
});
})
.catch((error) => {
let error_msg = error.message;
let code = error.code;
console.log(code, error_msg);
});
console.log("Function finished!");
}
function waitForLoading() {
setTimeout(() => {
$w("#preloader").hide("FadeOut");
}, 1500);
}
$w.onReady(async function () {
let data = null;
do {
data = await getTeamData();
} while (data != null);
console.log(data);
let group_name = data[0]["groupName"];
console.log(group_name);
$w("#groupNameTxT").text = group_name;
waitForLoading();
});
Had have anyone a similar problem and know how to solve this?
Thanks in advance!
Every promise chained with then must have a return value otherwise the return will be undefined.
In your case, you missed the return in all promises you have created. You must use return before the wixData variable in both calls.
This might help you understand how it works
Related
I have learned promise chaining with error handling to my satisfaction as per below first code section. But I am unable to get the following principle working with my Parse Cloud Code. My Cloud code works well but I have monster functions I need to refactor and organize.
My goal here
Is to have smaller cloud functions I can call without repeating code, as such, I want to have a function to trip an error if a user already is part of a group Parse.Role, otherwise I will add user to Role. Of course if error trips I want to stop the rest of the execution.
Please see second code snippets. I would appreciate some professional input.
function addOne(number) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(number += 1);
}, 1000);
});
}
/// Throws error unless number is 5
var test = addOne(5);
test.then(function(currentValue) {
currentValue ++;
if (currentValue > 7) {
return Promise.reject(Error("Initial value too high"));
} else {
return Promise.resolve(currentValue);
}
})
.then(function(value) {
value -= 2;
if (value < 5) {
return Promise.reject(Error("Initial value too low"));
} else {
return Promise.resolve(value);
}
})
.then(function(finalValue) {
finalValue ++;
if (finalValue != 6) {
return Promise.reject(Error("Not the sweet spot"));
} else {
console.log("You choose RIGHT!" + String(Promise.resolve("TEST")));
}
});
/// THIS IS ONE VERSION - NOT WORKING PROPERLY
/// Check if user is part of group
Parse.Cloud.define("mmDoesUserHaveGroup", async function(request) {
let userID = request.params.user;
// Search user class by user id parameter
let userQuery = await new Parse.Query(Parse.User).equalTo('objectId', userID).find({useMasterKey: true});
let userObject = userQuery[0];
// Check for user in all Roles
const query = await new Parse.Query(Parse.Role).equalTo('users', userObject).find({ useMasterKey: true })
.then(function() {
if (query.length != 0) {
return Promise.reject(Error('User is part of group already'));
} else {
return Promise.resolve();
}
});
});
Parse.Cloud.define("ccCreateGroup", async function(request) {
let userID = request.user.id;
const doesUserHaveGroup = Parse.Cloud.run("mmDoesUserHaveGroup", {user: userID});
doesUserHaveGroup
.then(function() {
return Promise.resolve("Create group action next");
})
.catch(error => Error(error.message));
});
/// ANOTHER VERSION - NOT WORKING EITHER
/// Returns false if user is not part of group or does not own group else error
Parse.Cloud.define("mmDoesUserHaveGroup", async function(request) {
let userID = request.params.user;
// Search user class by user id parameter
let userQuery = await new Parse.Query(Parse.User).equalTo('objectId', userID).find({useMasterKey: true});
let userObject = userQuery[0];
// Check for user in all Roles
const query = await new Parse.Query(Parse.Role).equalTo('users', userObject).find({ useMasterKey: true })
.then(function() {
if (query.length != 0) {
return true;
} else {
return false;
}
});
});
Parse.Cloud.define("ccCreateGroup", async function(request) {
let userID = request.user.id;
const doesUserHaveGroup = Parse.Cloud.run("mmDoesUserHaveGroup", {user: userID});
doesUserHaveGroup
.then(function(groupStatus) {
if (groupStatus) {
return Promise.reject(Error("User already has group"));
} else {
return Promise.resolve("Create group action next");
}
});
});
Even though I have no experience with Parse, judging only from syntax, I suppose that your problem lies is this section here:.
const query = await new Parse.Query(Parse.Role)
.equalTo('users', userObject)
.find({ useMasterKey: true })
.then(function() {
if (query.length != 0) {
return true;
} else {
return false;
}
)
Notice that as a general advice, it's not a good practice to mix Promise syntax with await unless you really know what you're doing.
In your case, inside the then part you are expecting that query is already set, which it won't be, since await is still, err, awaiting for the Promise to resolve (or at least that's what it seems to me).
What you probably want is something like this:
const query = await new Parse.Query(Parse.Role)
.equalTo('users', userObject)
.find({ useMasterKey: true });
return query? query.length > 0: false
there are two things we can start with.
First
you need the newto create errs
// creates a new instance of an error
new Error();
Second
from
est.then(function(currentValue) {
currentValue ++;
...
there to bottom there is no more promises as all is sequeensial
Best Practice Tip
never use things like
new Promise(async ()=>{...})
//OR
const some = async () => await asyncThing()
// or
const some = async () => {
const result = await asyncStuff();
return result
}
another problem in your code is that you use async with/out await. Eslint says it is an anti parttern
Parse.Cloud.define("ccCreateGroup", async function(request) {
let userID = request.user.id;
const doesUserHaveGroup = Parse.Cloud.run("mmDoesUserHaveGroup", {user: userID});
doesUserHaveGroup
.then(function(groupStatus) {
if (groupStatus) {
return Promise.reject(Error("User already has group"));
} else {
return Promise.resolve("Create group action next");
}
});
});
a better way of writting the method/function from avobe would be like
Parse.Cloud.define("ccCreateGroup", function({user: { id }}) {
return new Promise ((resolve, reject)=>Parse.Cloud.run("mmDoesUserHaveGroup", {user: userID}).then(function(groupStatus) {
if (groupStatus) {
throws new Error("the err");
}
return resolve("Create group action next");
});
}))
I might be missing some () or {}
I ended up refactoring and learning more about proper promise chaining with Parse promises here
Here is my refactored code, might help someone else thats new at Javascript, or not. Better read what the pros had to say in other answers ;)
/// Check if user is part of group role or if user owns group
/// Returns false if user is not part of group or does not own group else error
async function mmDoesUserHaveGroup(userID) {
return new Promise(async function (resolve, reject) {
// Search user class by user id parameter
let userQuery = await new Parse.Query(Parse.User).equalTo('objectId', userID).find({ useMasterKey: true });
let userObject = userQuery[0];
// Check for user in all Roles
const query = await new Parse.Query(Parse.Role).equalTo('users', userObject).find({ useMasterKey: true });
resolve(query);
});
}
/// First checks if user already has group
/// If user already has group then returns error and does not continue
/// If user does not have group, creates role, adds self to role
/// Then creates sync status objects with user set to true
Parse.Cloud.define("ccCreateGroup", async function (request) {
let user = request.user.id;
await mmDoesUserHaveGroup(user)
.then(
// No Parse error - mini method completed successfully
function (groupQueryCheck) {
// Check group status, if has group return error
if (groupQueryCheck.length != 0) {
return Parse.Promise.error("User is already part of a group");
}
},
function (groupQueryError) {
return Parse.Promise.error(groupQueryError);
})
.then(
function () {
let acl = new Parse.ACL();
acl.setReadAccess(request.user, true);
acl.setWriteAccess(request.user, true);
let adminRole = new Parse.Role("Group_" + user, acl);
return adminRole.save(null, { useMasterKey: true });
})
.then(
function (role) {
role.getUsers().add(request.user);
return role.save(null, { useMasterKey: true })
})
.then(
function () {
// Create Sync Status objects
let masterSyncStatus = { user: user, syncStatus: true };
let newUserSyncStatus = { user: user, syncStatus: true };
let masterArray = [];
let newUserArray = [];
masterArray.push(masterSyncStatus);
newUserArray.push(newUserSyncStatus);
// Initialize Sync Status class object by groupID
let SyncStatus = Parse.Object.extend("SyncStatus");
let syncStatus = new SyncStatus();
syncStatus.set("groupName", "Group_" + user);
syncStatus.set("masterSyncStatus", masterArray);
syncStatus.set("newUserSyncStatus", newUserArray);
return syncStatus.save(null, { useMasterKey: true });
},
function (error) {
return Parse.Promise.error(error);
});
});
Via API, I would like to list out the whole users. Each time can only have maximum 100 items for each page. So that I need to get the next_page url to rerun the function - runPages to collect the list. So the condition is when the next_page == null, then will stop the function.
In my code, I can get the next_page url. However, it doesn't run further. Can someone figure out what is the problem?
const runPages = async (values) => {
if (values.next_page != null) {
for (const field of values.results) {
row.appendChild(addCell(field.name));
row.appendChild(addCell(field.email));
tblbody.appendChild(row);
}
values = await checkPages(values.next_page); // get new values.data by url
runPages(values);
}
};
runPages(values);
const checkPages = async (value) => {
return new Promise((resolve, reject) => {
const getNewPageFromApi = async () => {
const GetUrl = `${value}`;
const Doorkey = { username: "XXX", password: "*****" };
try {
const response = await Axios.get(GetUrl, { auth: Doorkey });
if (response.data.next_page != null) {
resolve(response.data);
}
} catch (err) {
reject("no more data");
}
};
getNewPageFromApi();
});
};
I don't know if this is the answer you're looking for but since you're checking for values.next_page != null in the runPages function you can call the resolve(response.data) directly in the Promise inside checkPages function without checking for response.data.next_page != null.
I'm trying to retrieve the _id field with Wix Velo of my logged in user. The code is working, however the function I'm using is returning [object Promise] instead of the id string that I need.
import wixData from 'wix-data';
export function getID() {
return wixData.query("Picks") // My Collection
.eq("player", "Gary") // A field, and a cell
.find()
.then((results) => {
if (results.items.length > 0) {
let firstItem = results.items[0]._id;
console.log("Return ID: " + firstItem) // This returns the ID I need
$w("#text31").text = firstItem;
// return firstItem.toString();
} else {
console.log("No Items Found")
}
})
.catch((err) => {
let errorMsg = err;
});
}
console.log("Return the ID outside of the function: " + getID()) // This returns "[object Promise]"
I've tried to use await, but it just gives me errors.
Since top-level-await is not supported by all the browsers, I believe Wix didn't open that feature as well. Instead, wrap it with an async IIFE.
(async () => {
console.log("Return the ID outside of the function: " + await getID())
})();
This answer has more details about top-level-await
Just incase this helps someone else, the answer was in nested promises. I had to call the first function getLoggedInUserName() to get the user name, and then pass that to a nested function called getID().
Essentially, this was a scope issue, and also waiting for the promise to be resolved.
getLoggedInUserName().then((results) => {
let userName = results;
getID(userName).then((results) => {
let userID = results;
let pick = $w("#dropdown1").value;
let toUpdate = {
"_id": userID,
"pick": pick,
"player": userName,
}
wixData.update("Picks", toUpdate)
.then((results) => {
$w("#text33").show();
let item = results;
})
.catch((err) => {
let errorMsg = err;
console.log("Error: " + errorMsg)
});
})
})
.catch((err) => {
let errorMsg = err;
});
I am trying to build a React project where I have a component that fetches data from a remote server and plots it.
async function fetchRemoteData(name) {
let dataItem = null;
if (name) {
await fetch("/endpoint/" + name)
.then((res) => res.json())
.then((payload) => {
console.log("payload = " + JSON.stringify(payload));
dataItem = payload;
});
} else {
console.log("Error: No data for " + name);
}
return dataItem;
}
var RenderPlot = ({ match }) => {
let remoteObject = fetchRemoteData(match.params.data);
console.log("remoteObject = " + JSON.stringify(remoteObject));
// .... rendering code
// .... and other irrelevant stuffs to this question
}
If I run this code, on the console, I'm seeing the remoteObject is empty, but the payload inside the function fetchRemoteData() is not.
remoteObject = {}
payload = {"data":"some value"}
Now, let's say I have data stored in the browser (which is obviously I'm not supposed to do, but anyway) and if I fetch it using a generic function call, I'm not having problem.
function fetchLocalData(name) {
// read the data from a local js object
// and return it
}
var RenderPlot = ({ match }) => {
let remoteObject = fetchRemoteData(match.params.data);
console.log("remoteObject = " + JSON.stringify(remoteObject));
let localObject = fetchLocalData(match.params.data);
console.log("localObject = " + JSON.stringify(lovalObject.m))
// .... rendering code
// .... and other irrelevant stuffs to this question
}
and the output --
localObject = 4
remoteObject = {}
payload = {"data":"some value"}
So, what's happening is the code gets the localData from fetchLocalData(), then calls fetchRemoteData() but it doesn't wait if the data is received or not. And then, keeps doing whatever in the rest of the code.
What I want is, the code should wait until the remoteData is received and then proceed to the next steps.
How do I do that?
I can recommend you to store your received data in state of component like example below. If I understood you correctly -
const RenderPlot = ({ match }) => {
const [data, setData] = useState()
const fetchData = useCallback(async name => {
try {
const response = await fetch("/endpoint/" + name);
setData(response.data); // or whatever, depends on how you set your API up.
} catch (e) {
console.log(e)
}
}, [])
// in this case it works as componentDidMount;
useEffect(() => {
;(async function() {
await fetchData(match.params.data);
})()
}, [match.params])
return (<div>...your jsx plot implementation</div>)
}
According to this flow you can compose two fetching,
const fetch1 = useCallback(async () => {your first call api}, [])
const fetch2 = useCallback(async () => {your second call api}, [])
useEffect(() => {
;(async function() {
await fetch1...
await fetch2
})()
}, [match.params])
Or it can be like this
const fetchData = useCallback(name => {
try {
const response1 = await fetch("/endpoint1/" + name);
const response2 = await fetch("/endpoint2/" + name);
// this line would be read after both of your responses come up. Here you can make some operation with both data.
setData(response.data); // or whatever, depends on how you set your API up.
} catch (e) {
console.log(e)
}
}, [])
The reason is that ...return dataItem executes before that promise resolve the request. You need to use async await:
async function fetchRemoteData(name) {
let dataItem = null;
if (name) {
const res = await fetch("/endpoint/" + name);
dataItem = await res.json();
} else {
console.log("Error: No data for " + name);
}
return dataItem;
}
I am working on a site using Express.js, node.js, Axios, and ejs. I am making REST calls to a Oracle SQL REST services using Axios. I am having trouble working with Promises or Async/Await. I could use some guidance, if possible.
I have a repository layer to interface with the Oracle DB. For example:
dataaccess.js
const axios = require('axios');
exports.IsManufacturerCategory = function (categoryId) {
axios.get(`DB ADDRESS ${categoryId}`)
.then(response => {
console.error('GET IsManufacturerCategory categoryId = ' + categoryId);
console.error('Response = ' + JSON.stringify(response.data));
return (response.data);
})
.catch(rej => {
console.error('ERROR IsManufacturerCategory categoryId = ' + categoryId);
console.error('ERR = \n' + rej.data);
return (rej.data);
});
}
Which is called in my middleware. When I call var isManufacturerCat = exports.IsManufacturerCategory(categoryId); it is undefined. I am attempting to use the data retrieved from the Axios call to return a ejs view to my router, which I can provide if needed.
category.js
var isManufacturerCat = exports.IsManufacturerCategory(categoryId);
if (isManufacturerCat) {
var models = dataaccess.GetCategorySubCategories(categoryId);
return ("manufacturers", {
data: {
Canonical: cononical,
Category: category,
IsAManufacturerCategory: iAManufacturerCat,
Models: models
}
});
}
I am open to any advice in my project structure, usage of Promises, Async/Await, etc.
Thank you in advance.
EDIT
After working with some of the answers given, I have made some progress but I am having issues with layers of async calls. I end up getting into a spot where I need to await a call, but I am in a function that I am not able/do not want to do so (i.e. my router).
indexMiddleware.js
exports.getRedirectURL = async function (fullOrigionalpath) {
if (fullOrigionalpath.split('.').length == 1 || fullOrigionalpath.indexOf(".aspx") != -1) {
if (fullOrigionalpath.indexOf(".aspx") != -1) {
//some string stuff to get url
}
else if (fullOrigionalpath.indexOf("/solutions/") != -1) {
if (!fullOrigionalpath.match("/solutions/$")) {
if (fullOrigionalpath.indexOf("/t-") != -1) {
//some stuff
}
else {
var solPart = fullOrigionalpath.split("/solutions/");
solPart = solPart.filter(function (e) { return e });
if (solPart.length > 0) {
var solParts = solPart[solPart.length - 1].split("/");
solParts = solParts.filter(function (e) { return e });
if (solParts.length == 1) {
waitForRespose = true;
const isASolutionCategory = await dataaccess.isASolutionCategory(solParts[0]); // returns void
if (isASolutionCategory != undefined && isASolutionCategory.length > 0 && isASolutionCategory[0].Count == 1) {
// set redirecturl
}
}
else {
redirecturl = "/solutions/solutiontemplate";
}
}
}
}
}
else if (URL STUFF) {
// finally if none of the above fit into url condition then verify current url with Category URL or product url into database and if that matches then redirect to proper internal URL
if (fullOrigionalpath.lastIndexOf('/') == (fullOrigionalpath.length - 1)) {
fullOrigionalpath = fullOrigionalpath.substring(0, fullOrigionalpath.lastIndexOf('/'));
}
waitForRespose = true;
const originalURL = await exports.getOriginalUrl(fullOrigionalpath); //returns string
redirecturl = originalURL;
return redirecturl;
}
}
if (!waitForRespose) {
return redirecturl;
}
}
exports.getOriginalUrl = async function (friendlyUrl) {
var originalUrl = '';
var urlParts = friendlyUrl.split('/');
urlParts = urlParts.filter(function (e) { return e });
if (urlParts.length > 0) {
var skuID = urlParts[urlParts.length - 1];
const parts = await dataaccess.getFriendlyUrlParts(skuID); //returns void
console.log("Inside GetOriginalUrl (index.js middleware) FriendlyUrlParts: " + parts);//undefined
if (parts != undefined && parts != null && parts.length > 0) {
//some stuff
}
else {
// verify whether it's category URL then return the category local URL
console.log('Getting CategoryLocalUrl');
const categoryLocalUrl = await dataaccess.getCategoryLocalUrl(friendlyUrl); // returns void
console.log('CategoryLocalUrl Gotten ' + JSON.stringify(categoryLocalUrl)); //undefined
if (categoryLocalUrl != undefined && categoryLocalUrl.length > 0) {
//set originalUrl
return originalUrl;
}
}
}
else { return ''; }
}
index.js router
router.use(function (req, res, next) {
//bunch of stuff
index.getRedirectURL(url)
.then(res => {
req.url = res;
})
.catch(error => {
console.error(error);
})
.finally(final => {
next();
});
}
I am getting undefined in my console.logs after the awaits. I'm not really sure what I'm doing I guess.
Let's start with dataaccess.js. Basically, you're exporting a function that's doing async work, but the function isn't async. Most people want to be able to utilize async/await, so rather than have IsManufacturerCategory accept a callback function, it would better to have the function return a promise. The easiest way to do that is to make the function an async function. Async functions return promises which are resolved/rejected more easily than by returning an explicit promise. Here's how that could be rewritten:
const axios = require('axios');
exports.IsManufacturerCategory = async function (categoryId) {
try {
const response = await axios.get(`DB ADDRESS ${categoryId}`);
console.log('GET IsManufacturerCategory categoryId = ' + categoryId);
console.log('Response = ' + JSON.stringify(response.data));
} catch (err) {
console.error('ERROR IsManufacturerCategory categoryId = ' + categoryId);
console.error('ERR = \n' + rej.data);
throw err;
}
}
Note that I'm using console.error only for errors. Because you wanted to log some error related details, I used a try/catch statement. If you didn't care about doing that, the function could be simplified to this:
const axios = require('axios');
exports.IsManufacturerCategory = async function (categoryId) {
const response = await axios.get(`DB ADDRESS ${categoryId}`);
console.log('GET IsManufacturerCategory categoryId = ' + categoryId);
console.log('Response = ' + JSON.stringify(response.data));
}
This is because async functions automatically swallow errors and treat them as rejections - so the promise returned by IsManufacturerCategory would be rejected. Similarly, when an async function returns a value, the promise is resolved with the value returned.
Moving on to category.js... This looks strange because you're accessing IsManufacturerCategory from the exports of that module when I think you mean to access it from the import of the dataaccess module, right?
Inside this function, you should put any async work in an async function so that you can use await with function that return promises. Here's how it could be rewritten:
const dataaccess = require('dataccess.js');
async function validateManufacturerCat(categoryId) {
const isManufacturerCat = await dataaccess.IsManufacturerCategory(categoryId);
if (isManufacturerCat) {
const models = await dataaccess.GetCategorySubCategories(categoryId);
return ({
manufacturers: {
data: {
Canonical: cononical,
Category: category,
IsAManufacturerCategory: iAManufacturerCat,
Models: models
}
}
});
}
}
validateManufacturerCat(categoryId)
.then(res => {
console.log(res);
})
.catch(err => {
console.error(err);
});
A couple notes:
I changed the return value in the if statement to be a single value. You should try to always return a single value when working with promises and async/await (since you can only resolve/return one value).
I see lots of functions starting with capital letters. There's a convention in JavaScript where functions with capital letters are constructor functions (meant to be invoked with the new keyword).
Here is a working example. Note that you need a callback function to get the data when it is available from the promise
//client.js
const axios = require("axios")
exports.IsManufacturerCategory = function (url, callback) {
axios.get(url)
.then(response=>
{
callback(response.data);
})
.catch(error=>{
callback( error);
})
};
//app.js
const client = require('./client');
function process(data) {
console.log(data)
}
client.IsManufacturerCategory("http://localhost:3000/persons",process);
documentation