I'm currently dealing with AMD robustness.
My first problem was a typo in a require statement. I was working in the Firebug console and requested a non-existing module. When I corrected the typo and entered the statement again, "ready" wouldn't fire. In the Dojo issue tracking I was told this is by the spec (http://bugs.dojotoolkit.org/ticket/13825) and I understand the argument but it is really annoying.
This leads me to a more general question: What if a dependency can't be correctly resolved in a reasonable time?
What is the AMD answer to this question? Does it provide any way to implement a fallback in error case like it is possible with XHR? Some kind of onError handler?
I have a case where I require a resource from an possibly unreliable server and if I can't handle errors the state of my whole application is broken.
Thanks,
Martin
John is in the per-callback camp as I recall. I'm in the global error
handler camp (it is what requirejs uses). This is because:
It is difficult to ascertain a 404 or 500 error for a script because
of browser weaknesses. It is actually getting better, but IE is still
a stinking pile of pain. So the most you can hope for is a timeout,
not timely targeted errors.
I do not like having to mandate that AMD implementations need to trace
an bad module back to the require call (or calls) that may want to
know about it. That implies extra tracking in the loader to do this.
While on the surface it may make sense to add it to require(), I do
not think it makes sense to add to define() since the error handling
is specific to an app, not a defined module. I like that require and
define operate similarly, one just names a return value for use by
others (define).
So, for the define case, you need a global handler anyway, to handle
app-specific recovery.
I'm open to suggestions though, particularly if they are lightweight.
I appreciate that if an app does lots of dynamic require calls, it
would be useful to get targeted errors, but I'm skeptical that the
cost and reliability of doing so is worth it over just a global
handler.
James
John is in the per-callback camp as I recall.
It is difficult to ascertain a 404 or 500 error for a script becauseof browser weaknesses. It is actually getting better, but IE is still
a stinking pile of pain. So the most you can hope for is a timeout,
not timely targeted errors.
I do not like having to mandate that AMD implementations need to trace
an bad module back to the require call (or calls) that may want to
know about it. That implies extra tracking in the loader to do this.
While on the surface it may make sense to add it to require(), I do
not think it makes sense to add to define() since the error handling
is specific to an app, not a defined module. I like that require and
define operate similarly, one just names a return value for use by
others (define).
So, for the define case, you need a global handler anyway, to handle
app-specific recovery.
I'm open to suggestions though, particularly if they are lightweight.
I appreciate that if an app does lots of dynamic require calls, it
would be useful to get targeted errors, but I'm skeptical that the
cost and reliability of doing so is worth it over just a global
handler.
Ah you are correct, requirejs has the problem since it allows loading
non-AMD modules.
>>
>> I do not like having to mandate that AMD implementations need to trace
>> an bad module back to the require call (or calls) that may want to
>> know about it. That implies extra tracking in the loader to do this.
>
> Not entirely true. curl.js and the dojo loader (iiuc) have the necessary
> mechanics already. Also, if I understand how loadrunner works, it's got the
> infrastructure to track errors as well. I don't know how any other loaders
> work, so I can't speak about them.
> curl.js extends this functionality to its plugins, too. curl.js detects if
> the loader supports the error path by sniffing for a promise-like callback
> parameter in the "load" API. If it doesn't detect a promise, it assumes the
> plugin complies with the AMD standard (i.e. no error handling).
Right, so for me it is extra tracking since I assume that the global
handler is basically enough.
> I don't understand how one could apply a loader's error handler to define().
> That doesn't make sense to me. Error handling inside a define() is
> app-specific, but does not need to be done globally. (Maybe I don't
> understand what you mean?)
I meant define() is used to define modules, modules that can be used
by potentially anyone. Having an error handler in the define call
would not make sense, since each app that would include the module
probably has its own custom error handling logic. It is likely that
the app would like to have a global error handler, "if anything goes
wrong, call this function", and it would catch errors for those define
calls, so it can do so for require calls too.
If that is the case, then a per require() error handler does not buy
much, but I just realized recently I was assuming the app does maybe
one or two require() calls and has everything as defined modules. I
could see where if it does many require() calls, a per-require error
handler might be useful.
> I know you want to get 1.0 released. That's cool with me. :) I hope you'll
> take another look at this afterwards, though. This is an important
> oversight imho.
> Could we add it to the spec as an optional feature?
I would not mind trying to work out a spec for error handling, but it
should be treated like the loader plugin one, something that is
separate from the core AMD API, but not unusual to find in an AMD
loader. So if you want to spearhead that work and add tests to the
amdjs-tests group for it, I am open to it.
For now, I'm fine with requirejs not supporting it. If it did, since
requirejs supports plain script loading too, it would likely only get
triggered on timeout errors.
James
I'm coming around for having a per-require() call error handler, in the form of:
require([], function () { /*this is the ok handler */ }, function
(err) { /*this is the err handler*/ });
I believe curl supports something like this now, and I'll be
prototyping this for a requirejs 1.1 release.
Are other implementers OK with this approach? Any concerns? If so, we
should make a shot at documenting it in the require API doc as an
implementer option.
James
Richard
Ah, good question. What about both? If we want to talk about what we
actually have specified though, so far that is only the local require,
but I'm thinking of allowing it for both in requirejs.
James
1) What would the default behavior be if no error handler was provided ?
2) Is there any particular format/type for the err parameter passed to
the error handler ?
3) Earlier in the thread you mentioned this should not be part of the
core AMD API. As we are modifying the signature of require do you still
think that can still be possible ?
Richard
I'm fine with this being undefined for now. For requirejs, I'll
probably still have the global handler too.
> 2) Is there any particular format/type for the err parameter passed to the
> error handler ?
I think it would be fine to start with just "must be of type Error.
I'm open to locking it down more once we have some experience with the
type of errors. Hopefully we'll share what we each end up returning.
> 3) Earlier in the thread you mentioned this should not be part of the core
> AMD API. As we are modifying the signature of require do you still think
> that can still be possible ?
I meant as part of the main AMD define() API page. If it ends up on
the local require, then this page should be updated:
https://github.com/amdjs/amdjs-api/wiki/require
and that it should be mentioned as an optional thing. Well I suppose
it will by default be optional -- if you pass in an error handler and
the loader does not support it, that error handler will never be
called. :)
James
There is a disagreement amongst AMD implementors about this. Some of us advocate a global amdLoader.onError handler. Others advocate a local error handler. Some examples of how this might work:// simple callback function for errors, "errback"require(["mymodule"], callback, errback);// CommonJS Promises/A (e.g. dojo/Deferred) or similar (such as jQuery.Deferred)require(["mymodule"]).then(callback, errback);// promises, another wayvar dfd = new Deferred();require(["mymodule"], dfd.promise);dfd.then(callback, errback);
I still think they add more complication to the API than what is
needed, and prescribe too much of an implementation. I like using
promises in my own app code though.
James
I am working on this too for the next requirejs version.
James
This is for communicating that a loader plugin cannot fulfill a
request to it's load() call?
I favor attaching something to the load() function passed to the plugin:
define({
load: function (id, require, load, config) {
//on success:
load(value);
//on error, just an example of possible api name
load.error(err);
}
});
I favor hanging it off load since it is related to the resolved value,
where the require passed in is for the plugin to require other
dependencies to figure out the resolved value.
James