Hey @boston85719: This is actually a really interesting question. But note that the answer (with respect to List Shifter) is complicated because of the fact that it loads Lists (any element that loads lists will also function similarly to what I describe below).
I did a little (actually a lot) of experimentation to determine the following:
So, first: When does a plugin elementâs initialization function run? How is the order of this determined?
A plugin elementâs initialization function starts when it first becomes visible on the page. (We know this from the plugin API docs and also by experience.)
But when that happens is determined by the elementâs order in the DOM (the Document Object Model - the structure of that page that you can see in the Elements view in your dev tools).
It seems that the Bubble Editor pretty much structures the DOM in order of element creation. So, yeah, basically, âolderâ plugin elements will start initializing before newer ones.
Letâs say you put three List Shifters on your page, LS1, LS2, and LS3, in that order, at the same page level (for example, directly on the page), they will run their initialize
functions in that order.
And, as a result, they will start their update
functions in that order. But their update
functions will not necessarily complete in that order.
How can we change these elementsâ order in the DOM?
Now, hereâs something interesting: Moving the elements around on the editor canvas doesnât change their positions in the DOM. (Like, moving LS1 to the right of LS3 or moving it visually âaboveâ LS3 doesnât change LS1âs order in the DOM (its position on the canvas changes, but not where it lives in the DOM).
If we cut and paste one of those List Shifter elements, we essentially create a new element and that elementâs DOM order will be lower than the ones we previously created and so it will initialize after them (e.g., we could cut LS1 and paste it and now the init order will be LS2, LS3, LS1). But of course, this will destroy any workflows using LS1 and cause other mayhem (should anything else refer to it).
However, there is a non-destructive way to change an elementâs order in the DOM: If we do something like create a new group (so we have a newer element, which will have a lower position in the DOM), if we drag LS1 inside of that, now what will happen is the initialization functions will run in the order LS2, LS3, LS1 (and, indeed, if you inspect preview page using the Element view, you will see that the order of the elements in the DOM is LS2, LS3, and then LS1 further down inside of that new group). And doing this doesnât break any references to LS1. Hooray!
So that all makes sense when you think about it, but itâs surprising when you actually first look into it!
Another thing we could do: We could completely control when a plugin element starts initializing and subsequently updating by setting it to NOT be visible on page load, but then setting it to visible based on some other event. (More on this at the end of this reply.)
QUICK EXPLAINER about initialize and update: In element plugins, we have a function initialize()
that gets executed, as I said, just once, when the element becomes visible on the page. Once that is complete, Bubble calls the elementâs update()
function.
Initialize()
is where we do things like, if the plugin is a visual element, we would set up our canvas (the visible part of the element), we might initialize certain variables, etc. Now, my plugins donât usually have a visual element so they just have an empty canvas, but I also define all of the functions that my plugin needs in initialize and I also define all my defaults for things here and I put all this stuff on to the object called instance
that Bubble functions can pass around. (Basically inside my initialize function there is a wrapper function, inside of which I define all my functions and stuff. Then, I run that function, which (handwave) basically compiles all my code.)
This initialize routine executes in a VERY short amount of time. (On my machine, the initialize function for List Shifter completes in somewhere between 0 and 1 ms.) Then Bubble calls the pluginâs update()
function. The update function is where we read the fields defined in the main plugin interface. And, unless we do some clever scripting to prevent it (and thereâs not much reason to do this, by the way), anytime that the field values change, the update()
function gets called again.
What we do in the update
function is read the values of the main interface fields and configure our plugin to do⌠well⌠whatever the plugin does. So, in my plugins, I read all your settings and (handwave) do whatever stuff needs to be done as a result.
At the very end of that function, I set a property called âinitializedâ to true, publish that to the pluginâs âInitializedâ exposed state, and then trigger an âInitializedâ event (sometimes I call that event âInitialized/Updatedâ because the event triggers on the first update as well as subsequent updates).
So, List Shifterâs Initialize/Updated event means that (1) not only has the initialize()
function run, but (2) the update()
function has also completed, and now weâre ready to fire Actions at the element. (This is true of all of my element plugins that have Actions.)
// END EXPLAINER
Now, you might think that, because JavaScript is single-threaded and the update()
function is just a normal, run-of-the-mill synchronous JavaScript function, that our update routines will always complete in sequence.
However, thereâs one complexity: If any of our fields are Lists or other types of objects (like Things) and we need to fetch them, Bubble will in most cases have to go to the database to get them (unless something else happens to have already fetched the same data). And what happens is that Bubble asynchronously begins fetching the requested data. This data comes down in chunks and Bubble will actually interrupt the update()
function until all of our requested data is loaded. (So literally what happens is that our function gets called multiple times and subsequently interrupted, until the data loading is complete. If youâre interested, you can learn more about that in the Bubble docs here.
The net-net of this is that letâs say that LS1 is loading a big olâ honkinâ list of data (like, a Search whose results are 1000 Things). If LS3 is just loading a tiny list, LS3 may easily complete its update()
function well in advance of LS1, and maybe even in advance of LS2.
So: The elementsâ initialize()
functions are sequential and an element earlier (higher up) in the DOM will start and end that function before one later (lower down) in the DOM, but those events are not separated by very much time in most cases.
And while the update()
functions will start executing in the same order, depending upon what the plugin does, those functions may finish in a different sequence.
So, we could imagine a case where weâve got several (maybe many) List Shifters on our page and most of them are loading little things (maybe just a scalar value or a small list that youâre using to feed the headers in a table or something), but one of them is going to do a big-long search.
Letâs say that big-ass search is being done by LS1 (doesnât matter where it lives in the DOM). We could it to be invisible on page load and then make it visible once all the other LSâs have gone to Initialized. (e.g., workflow trigger âLS2âs Initialized and LS3âs Initialized and LS4âs initializedâ â show element LS1)
While I donât think that this will result in LS1 becoming fully populated any sooner (in fact, thatâs sort of impossible), it might make for a better UX as all of the things that depend on the âlightweightâ List Shifters will be happy. You could test the difference between these scenarios using Debug Buddy to benchmark the time it takes for LS1 to reach the Initialized state in both scenarios.