Closed uptimestar closed 6 years ago
For error callbacks you need to use the more advanced syntax (define success/error
functions in an object argument). Here's a working example that mixes and matches bundles and urls:
https://jsfiddle.net/muicss/ts5fv67L/
It is not related to error handling. It is related to using bundle ID. Here is the bug reproduced:
loadjs(['bundle1','//cdn.jsdelivr.net/npm/fullpage.js@2.9.6/dist/jquery.fullpage.css'], 'bundle2', {
success: foo,
async: false,
error: function() {alert('not an error, bundle is defined')}
})
setTimeout(function(){
loadjs(['//code.jquery.com/jquery-3.3.1.min.js'], 'bundle1')
}, 1000
)
function foo() {
alert('success')
}
The way complex scripts get loaded and bundles defined can get complex. The page for me needs a success function and has different team working on it. So the bundle1 gets loaded a bit later. Since loadjs does not see, it assumes it is a lib. So an bug.
One way to fix is to be able to define a bundle, but don't load. This way I can declare the bundle and dependencies, and then just ask for something later.
(also it be nice to be able to load an image)
In your example, bundle1
gets defined after you list it as a dependency so loadjs isn't aware that it is a bundle name and treats it as a url instead. To change that behavior, loadjs would have to make assumptions about urls and bundle names which might cause problems for some developers.
One way to execute code only after a bundle has been defined is to use the .ready()
method:
loadjs.ready('bundle1', function() {
loadjs('//cdn.jsdelivr.net/npm/fullpage.js@2.9.6/dist/jquery.fullpage.css', function() {
alert('success');
});
});
setTimeout(function() {
loadjs(['//code.jquery.com/jquery-3.3.1.min.js'], 'bundle1');
}, 1000);
One drawback of the code above is that it will load the files in series. If you want to load the files in parallel but you don't know if your colleague's code has executed yet then you can use .isDefined()
to execute different code paths:
if (loadjs.isDefined('bundle1')) {
loadjs(['bundle1', '//cdn.jsdelivr.net/npm/fullpage.js@2.9.6/dist/jquery.fullpage.css'], {
success: function() {alert('success');},
error: function() {alert('error');},
async: false
});
} else {
loadjs(['//code.jquery.com/jquery-3.3.1.min.js', '//cdn.jsdelivr.net/npm/fullpage.js@2.9.6/dist/jquery.fullpage.css'], {
success: function() {alert('success');},
error: function() {alert('error');},
async: false
});
}
Please note that in order for async: false
to work with the first conditional code block above your colleague must have defined bundle1
with async: false
as well.
If loadjs were to support images, do you have any suggestions for the syntax?
Yes we use code work arounds now. The idea is to make it opposite of 'code splitter'. The uses is in pug/jade you do include ../parts/Footer;pug to make html or you do:
so hard to know easily what gets run first. Lots of chicken or the egg issues. Hard to reason on order of execution of snnipets.
A ideal solution would be to declare a few basic bundles we need, BUT NOT load them. For example in head define the few basic bundles. And then when you need them included, execute. Ex: loadjs.define(.....) and loadjs.execute(....). Maybe introduce new method signatures. That was what I suggested above "declare the bundle but not execute"
Your idea of determining if it is an asset/resource or a 'bundle' would also work: Bundle never contain a ".". asset and resource always do: ex: main.js. But it is not perfect as ... it still loads, and I may not want to load yet. I may want other things displayed first.
thx for heads up on async. A feature request would be that if anywhere in chain is async false: it is false.
the image is not a must have like above. above lets you work on a complex project with a large and unwieldy remote teams. But it be nice to load media just like we load css with no changes in syntax. So lets say - before I make some div visible: I finish loading css, needed .js, SVG, and the background video. (we even use google fonts via google webfont.js so it does not block parallel load of css, but webfont likely can't be a part of load js)
Can you explain the problem you are trying to solve in more detail? If I can understand your problem better then maybe I can suggest a different solution.
I think it is quite a bit of low detail is given. Maybe higher level as a different POV? A large distributed team on complex pug project that tried webpack code splitting Now using loadjs instead, works much much better. Lots of things required jquery and such, and we also use pug includes a lot. We use work around for async loads of bundles. Some things like fullpage.js requires jq and another lib BEFORE it is loaded. But that is not needed on each page. Also, we check load times a lot view browser tools to check time to screen. If we can get rid of workarounds and just be able to declare few basic bundles that others devs can 'extend' or depend on, that would be nice. So in pug we can say loadjs([baseBundle, fpBudnle, myeffect.js] function(//unhide div). This all works, but is not elegant. If you add some more bundle features, code would look nicer.
Ok, thanks for the overview. I think loadjs has the basic primitives you need and it's just a matter of figuring out where to use them.
Based on your description I would define the bundles in the <head>
to take advantage of the browser's built-in parallel load/sequential execution mechanism:
<head>
<script src="/path/to/jquery"></script>
<script src="/path/to/baseBundle"></script>
<script src="/path/to/fpBundle"></script>
<script>
// manually declare bundles
loadjs.done('jquery');
loadjs.done('baseBundle');
loadjs.done('fpBundle');
</script>
</head>
And then wrap your code with .ready()
to ensure that the code won't execute until the dependencies have been met:
loadjs.ready(['baseBundle', 'fpBundle'], function() {
loadjs('/path/to/myeffect.js', function() {
// unhide div
});
});
In general, .ready()
and .done()
can be used together for more fine grained control over bundle definition and execution times.
Does not help. No worries. It is Open source, when it hurts enough we'll fork source.
On Sat, Mar 10, 2018 at 5:52 PM Andres Morey notifications@github.com wrote:
Ok, thanks for the overview. I think loadjs has the basic primitives you need and it's just a matter of figuring out where to use them.
Based on your description I would define the bundles in the
to take advantage of the browser's built-in parallel load/sequential execution mechanism:And then wrap your code with .ready() to ensure that the code won't execute until the dependencies have been met:
loadjs.ready(['baseBundle', 'fpBundle'], function() { loadjs('/path/to/myeffect.js', function() { // unhide div }); });
In general, .ready() and .done() can be used together for more fine grained control over bundle definition and execution times.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/muicss/loadjs/issues/60#issuecomment-372073708, or mute the thread https://github.com/notifications/unsubscribe-auth/AeT3VFXpu09iS62s7sBX4XGCH-57bC2sks5tdFkwgaJpZM4SjipS .
You should be able to implement complex dependency chains in a simple way by wrapping your code in .ready()
and using .done()
for execution. For example, here are three bundles and a plugin ("bundle1a.js", "bundle1b.js", "bundle2.js", "effect.js") where one bundle depends on another and all depend on jquery:
// bundle1a.js
loadjs.ready('jquery', function() {
// bundle1a code goes here...
loadjs.done('bundle1a');
});
// bundle1b.js
loadjs.ready('bundle1a', function() {
// bundle1b code goes here...
loadjs.done('bundle1b');
});
// bundle2.js
loadjs.ready('jquery', function() {
// bundle2 code goes here...
loadjs.done('bundle2');
});
// effect.js
loadjs.ready(['bundle1b', 'bundle2'], function() {
// effect code goes here...
});
Once the files have been implemented in this way you can load them into the DOM in order you want, synchronously/asynchronously, in series/parallel, with/without loadjs and in javascript or in html.
Thank you for doing this example. It perfectly illustrates what the problem is. Lets take a look at first part:
loadjs.ready('jquery', function() {
// bundle1a code goes here...
loadjs.done('bundle1a');
});
What you did is // // bundle1a code goes here... where there is more load js code. Clever. But our developers include graphic designers. HTML people. Art directors/creatives. jr jquery developers and such. The nesting is complex code for them- and this is just 3 levels deep you showed with majority of code you did as //write code here. If you did add code, it would look quite confusing to ... a CSS person. We need code that makes the user feel more confident. I need them focused on building the site and making the site look nice. This example is not for those developers, it takes away from their work focus. What we need is a more syntax sugar that make is look easy to use bundles to control delayed loading. I'd like for the user of loadjs to be the one that feels clever. (at the expense of suffering of loadjs maintainer ).
Would it help to be able to define a bundle inline?
// bundle1a.js
loadjs.ready('jquery', 'bundle1a', function() {
// bundle1a code goes here...
});
// bundle1b.js
loadjs.ready('bundle1a', 'bundle1b', function() {
// bundle1b code goes here...
});
// bundle2.js
loadjs.ready('jquery', 'bundle2', function() {
// bundle2 code goes here...
});
// effect.js
loadjs.ready(['bundle1b', 'bundle2'], function() {
// effect code goes here...
});
I think you are saying your done is 'auto'. It may be more readable if it was different order, done as last
loadjs.ready(['bundles1', 'bundle2'], function() {
// more code and loadjs
}, 'bundles3'
)
I think an improvement for ready. But still as soon as I define a bundle it loads. If I can define a bundle that is lazy. loadjs.define( ...) that waits till it sees a ready().
How would the wrapping ready know when some loadjsit is wrapping in 'code' is done? I don't think that you can implement that.
But still as soon as I define a bundle it loads. If I can define a bundle that is lazy. loadjs.define( ...) that waits till it sees a ready().
Have you tried using RequireJS?
How would the wrapping ready know when some loadjsit is wrapping in 'code' is done? I don't think that you can implement that.
Internally, loadjs will call .done()
after the wrapped function is executed.
@uptimestar This issue came up in another thread (https://github.com/muicss/loadjs/issues/62). Here's some custom code that you can use to implement a lazy loader with loadjs
:
var bundles = {
'bundle1': ['file1', 'file2'],
'bundle2': ['file3', 'file4']
};
function require(bundleIds, callbackFn) {
bundleIds.forEach(function(bundleId) {
if (!loadjs.isDefined(bundleId)) loadjs(bundles[bundleId], bundleId);
});
loadjs.ready(bundleIds, callbackFn);
}
require(['bundle1'], function() { /* bundle1 loaded */ });
require(['bundle2'], function() { /* bundle2 loaded */ });
require(['bundle1', 'bundle2'], function() { /* bundle1 and bundle2 loaded */ });
Thank you for keeping it in consideration. Does the last 3 lines see the bundle names that is in closure in first 4 lines? It be very nice to add some syntax sugar for the 'diverse' .js developers to make it easier to read/teach.
Yes, the require()
method has access to the bundles
object. Here's a working version:
https://jsfiddle.net/muicss/4791kt3w/
You can modify this code to change the syntax. Let us know when you find a syntax you like.
Thx again. I think loadjs.require(....) is fine sugar.
I think of this more as a better/manual webpack code splitting w/ CDN than requirejs. Now if you let me load an image asset like I can load css, perfect. I can now 'loadjs.require()' css, image and .js before I $.show() a div or such.
Ok, if you want to add this to your implementation you can attach require
to loadjs
:
loadjs.require = function(bundleIds, callbackFn) {
bundleIds.forEach(function(bundleId) {
if (!loadjs.isDefined(bundleId)) loadjs(bundles[bundleId], bundleId);
});
loadjs.ready(bundleIds, callbackFn);
}
@cekvenich I added support for image loading to the loadjs:img
branch of the repo:
https://github.com/muicss/loadjs/tree/img
Here are links to loadjs
payloads with the new code:
https://cdn.rawgit.com/muicss/loadjs/img/examples/assets/loadjs/loadjs.js
https://cdn.rawgit.com/muicss/loadjs/img/examples/assets/loadjs/loadjs.min.js
It passed these tests in modern browsers and IE9+: https://github.com/muicss/loadjs/blob/img/test/tests.js#L352-L467
Let me know if you notice any issues with the new features.
I'll test! Is 'require' sugar included? This way the examples of lazy loading are on your site.
Sorry, the require
method is not in the library but you can copy the code if you want to add it to your site.
@cekvenich Have you had a chance to try out the img loading feature?
It is on my list - during week I am busy and will be out of town for a few. Don't worry!
Ok, no worries. It's pretty well tested and backwards compatible so I think I'll push to master soon.
This is documented as item 7, but is not shown in examples.html.
It appears to fail for me, as it tries to load the bundle name via http. So I check if my mistake by running examples. The examples run, but they don't show above documented use case.
so this modified example fails but is documented as working.