wait for wix-data.query() to complete.

I am new to wix and javascript, though with years of experience as a BACKEND engineer w/many languages, including Python & Ruby, interpreted languages that make javascript relatively familiar.

What’s VERY foreign (I have ZERO front-end/UI experience) is the approach, necessary as it is in the browser world, of the way the single thread model supports asynchronicity.

I’m used to a wealth of support for asynchronous/parallel processing through threads.

The Promise mechanism appears to be a ‘SWEET’ solution to the problem of asynchronicity in javascript.

Only I can’t wrap my head around how I deal w/this mechanism to support the need to know when a query has completed. I love the fact that I can launch the query at the start of page load or onReady(), but ALSO in onReady() (or in page load really) I need to be able to get the results of the query to hide/show certain elements based on attributes of a particular user logged in which are stored in my ‘Members’ table.

I get that the ‘then’ method only runs on query completion, AND I’ve seen using the following code that it sometimes completes BEFORE this while loop:

while ( (membersDB[‘val’] === “N/A”) && (indx < 100000000) ) {

and sometimes AFTER, but it appears if it DOESN’T complete before the loop, that no matter how long I wait in the loop (and this IS a VERY simple wait: I’ve used more significant ones using console.log(), etc.) that the query completion DOESN’T complete during the loop. I always get indx = 0 OR indx = whatever limit I set. Sounds like black magic, but THERE MUST BE a better way to wait for the query completion. I’ve seen the discussions of async/await, but I haven’t been able to figure out how to use it.

The query method:

export function getTable(collection, returnObj) {
wixData.query(collection)
.find()
.then( (results) => {
returnObj[‘val’] = results;
returnObj[‘end’] = new Date().getTime();
console.log(“getTable(” + collection + ") returned at " + returnObj[‘end’]);
} )
.catch ( (err) => {
console.error('getTable ERROR: ’ + err);
})
}====================

The code that calls it (starting w/page load):

import {queryTable} from ‘public/db_helper.js’;

let startTime = new Date().getTime();
let membersDB = {‘val’: “N/A”, ‘start’: startTime, ‘end’: null};

getTable(‘Members’, membersDB);
console.log(“firstLoad: membersDB = " + membersDB[‘val’] + " @” + membersDB[‘start’]);

$w.onReady(function () {
var enterTime = new Date().getTime();
console.log("onReady(): userEmail = " + userEmail[‘val’] + “; membersDB = " + membersDB[‘val’] + " @” + enterTime);
var indx = 0;
while ( (membersDB[‘val’] === “N/A”) && (indx < 10000) ) {
indx++;
}
var endTime = new Date().getTime();
console.log("onReady2(): indx = " + indx + "; userEmail = " + userEmail[‘val’] + “; membersDB = " + membersDB[‘val’] + " @” + endTime);

Hi thomas.lloyd4,

The JavaScript runtime is basically running an event loop.
Whenever an async operation completes, an event is added to the event queue, so in your example the completion of the data query is that event and the event handler that will be called when the event will be processed is the function passed to the then function of the promise.
So if the query returns while the loop is running, the event for it will be added to the queue, but will only be processed after the current event processing ends (in your case, that’s the ready event, which runs the code that contains the long loop).

Hope this helps :slight_smile:
Ofer

Thanks, Ofer.

Yes, that does help. But it essentially brings up more questions:

  1. the onReady() method for the page has been launched as an event by the event handler. Correct?

    b) Meaning that no resources that aren’t available (DB records, etc.) at START will be available at all, at least not during the duration of onReady() execution. Correct?

  2. So if I have to do a simple action like hide or show an element on the page based on an attribute that I need to fetch from a DB table, then my query.then() method should call a method that has access to a global object (my membersDB object) that NOW should contain a reference to the element to be shown/hidden AND any other attributes needed to find the necessary info from the DB table to make the hide/show decision.

AND that global object (membersDB) would contain state showing whether the page’s onReady() method has been called yet, so that it DOESN’T try to hide/show the element if onReady() NOT yet called (shouldn’t yet have the element reference: this is because I will code a generic function to do this for any page), and it DOES perform the hide/show if onReady() already called.

Hope you understood that. If so does that sound right? Or is there a simpler way to handle such transitions?

==============================================

A couple more related questions:

  1. The code I’ve written ABOVE the onReady() method that I’ve referred to as ‘page load’ code. PRESUMABLY that is run by the event handler as well as another event. Yes?

b) Is that ‘page load’ code run for each page only as it is actually referenced/called? And if so is that only run the FIRST time the page is loaded (as opposed to each time the page is refreshed for one reason or another).

OR are all the related pages loaded (and code run) at the start of a session, which seems very unlikely. 
  1. Considering that I need to load the same DB collections (or at least perform similar queries on the same collections) on many if not all of my pages, it is my understanding that each page will have to load those collections again when launched. There is no way to preserve global objects across pages, objects/Arrays that could store DB rows? I see that wix-storage and also the ‘window’ object can maintain ‘global’ state for the duration of a session across pages, but neither allows storing objects, only primitive datatypes (String, Number, Boolean). And the ‘window’ object I don’t think allows adding dynamic attributes but has a static set of attributes it persists. Right?

Thanks for your time.
Tom

Hi Tom,

  1. To clarify the terms: when you call $w.onReady, you are registering the function you pass to it as an event handler to the “page ready” event. The function you passed will be called when the JavaScript runtime processes the “page ready” event that is emitted by the Wix Code platform.

you indeed can’t rely on stuff being available during the onReady handler if you do not explicitly write code that waits for that stuff to arrive.

  1. You can delay the page display until your data is available by returning a Promise from an onReady handler. The platform will wait for the promise to resolve before displaying the page. So you can query the DB inside the onReady handler, change the visibility of the related component, and then resolve.
    Something like:

$w.onReady(function() {
return wixData.query(collection)
.find()
.then( (results) => {
if (someLogicBasedOn(results))
$w(‘#myComponent’).hide();
} );
});

As to the “related questions”:

  1. What you refer to as “page load” code will run whenever that page starts to load, but a bit earlier in the page’s lifecycle than any “onReady” handlers - before the components are ready to be interacted with.
    It is executed each time the user switches to that page.

  2. I think using ‘wix-storage’ is the way to go here. Note that you can possibly use ‘JSON.stringify()’ and ‘JSON.parse()’ to serialize/deserialize your objects to/from strings.

Good luck!
Ofer

Hmmm. Lots more to think about here:

  1. You say: ‘you indeed can’t rely on stuff being available during the onReady handler if you do not explicitly write code that waits for that stuff to arrive.’

I thought the previous conversations determined that there WAS NO WAY to ‘write code that waits for that stuff to arrive’. Since the ‘onReady’ function is running in an event, and any DB query’s ‘thenI)’ function is a NEW event placed on the queue which must wait for the CURRENT event (my onReady code that LAUNCHED the query) to complete before my query’s then() event will pop up in the queue to be run.

???

1a) My original question was HOW to ‘write code that waits for that stuff to arrive.’ If there IS such a way, please tell me. I have lots of contexts in our site where I need to query DB data before I can complete a line of processing. My current thinking is to query all the ENTIRE collections either at a particular page load or even at the FIRST page load and persist all of that data for the session w/wix-data.

ALTERNATIVELY, I’m also considering using closures that would be called by query then() function to complete processing on the completed query. But that might only work to complete processing in ‘some time AFTER’ the code that sets it up, and never in a synchronous fashion. THAT said, I’m pretty sure that almost all of our processing can be done in this lazy fashion, and that there is very little processing (hopefully none) that REQUIRES a synchronous/sequential processing pipeline.

  1. Per ‘related question 2)’:

What are the constraints on memory usage of wix-storage? Particularly limits on a single String that I would store. As I said above, I am considering stringify()'ing entire collections to store in wix-storage. We are by nature a very small business, so I do NOT envision any of the collections I’d want to persist in this way to grow to 64K. Probably not even 64K for all combined collections to persist any time soon. Don’t like planning without considerations for scaling, but we’re likely years from needing to worry about going beyond 64K.

Oh, and thanks again, Ofer, for your time and patience.

A correction to what I just wrote above (though you likely understood what I INTENDED):

‘My current thinking is to query all the ENTIRE collections either at a particular page load or even at the FIRST page load and persist all of that data for the session w/wix-data.’ => NOT wix-data, but wix-storage.

  1. The example code I wrote before is the way to delay the display of the page until you get what you need from the DB. If you return a promise from an onReady handler, the framework will wait for the promise to resolve before displaying the page. Notice I emphasised the “return” keyword there.
    In terms of sync/async, the function you pass as an “onReady” handler returns immediately. Its return value is a Promise object (which is the chained promise returned from the “then” method which you called on the promise returned from the “find” method).
    The framework delays the display of the page until this promise becomes resolved.

  2. I don’t really know the limitations, but I would advise against trying this optimization prematurely. First try the approach from my example, that gets the data on page load and see if it is good enough for your needs.

BTW, the example code above could be rewritten and simplified using async/await like this:

$w.onReady( async function() {
const results = await wixData.query(collection).find();
if (someLogicBasedOn(results)) {
$w(’ #myComponent ').hide();
};
});

Ofer,

I literally started using ‘code’ 2 days ago (I am a nurse by profession - not a ‘coder’ or whatever you guys are(!), never built a website, etc etc . . . . HOWEVER, I am a very keen reader, very quick learner and managed to make a header that changes on scroll using ‘code’ within a day :slight_smile: I am slowly starting to understand ‘code’, and thank you for what you have supplied - but of course, as a novice, I am not understanding what is surely obvious to you all, and hope your expertise can help me! I have created a ‘Gif’ (that I want to go around and around on ‘loading’), but how do I incorporate this into the code? Also, do I create a ‘page’ - how do I make a ‘preloading screen’ that is going to come BEFORE my home page?

Basically, how (and please, baby language!) do I USE your code - where do I put it, and what do I have to create or do in order to make it work? Do I make a box or an overlay or something on the ‘home’ screen?!?

And and ALL help massively appreciated.

Thanks so much,

Rachel