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
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
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.