first-class support for asynchronous tests #622

Closed
alanhogan opened this Issue Jun 21, 2012 · 14 comments

Projects

None yet

7 participants

@alanhogan

Modernizr doesn’t wait until running its tests, does it?

I see some of the “community add-ons” like the data-URI test note in their source that they are asynchronous.

I’m experiencing some sort of timing error and want to rule out Modernizr. Is there any way to check if tests such as these have completed?

I notice this particular test does not define itself as a Modernizr test until the data-uri image has either failed or completed:

  Modernizr.addTest('datauri', function () { return (datauri.width == 1 && datauri.height == 1); });
  //or
  Modernizr.addTest('datauri', function () { return false; });
  1. Should I be able to detect undefined on Modernizr.datauri until then?
  2. Is there some way I can detect if Modernizr (+ community add-ons) is done running all its tests? It doesn’t look like it. Should I file that as a feature request?
@paulirish
Member

I renamed your issue. hope that's cool.

check out the last four or so comments in #539 where we discuss an event system for Modernizr tests.

as to your questions..

  1. nope.
  2. no way to do it, really. We can use this ticket as the feature request.
@alanhogan

Of course it’s cool. This new, smart issue title makes me look like a genius.

Thanks for confirming.

@jokeyrhyme
Contributor

Is there a plan yet on how this might work in practice?

We could have addTest emit events for each test as it completes, however that doesn't help in cases where code that requires the test result hooks the event after the test has finished. This may still be useful though.

We could have addTest take a callback as it's 3rd parameter, or have a new addAsyncTest method with a 3rd parameter. It would be up to your asynchronous code to invoke this callback when you have your test result, passing it as the first parameter.

I like a callback based approach, because it's somewhat easy for consumers to layer Promises or events on top of that where necessary (and I hope by now the average JavaScript developers understands the asynchronous callback "pattern").

Of course, that only really solves it for cases where consumers can modify the calls to addTest, which probably too tightly coupled to be a good idea. I guess we need a way to register a generic asynchronous test callback with Modernizr, and maybe have addTest call that same callback over and over again with different parameters as necessary?

Hmmm, tricky.

@jokeyrhyme
Contributor

What if we had generic callbacks for when a test is first added, and a separate callback for when a test completes? That's probably about as flexible as any system would need to be, but now I'm thinking 2 callbacks to configure might be more complex than necessary...

@paulirish
Member

in callbacks vs events we could have a Promises style setup instead... that
would tackle this timing issue

@jokeyrhyme
Contributor

Something @alanhogan mentioned in the original request got me thinking: should there be an option to stop Modernizr from automatically running tests as they are added?

If we can add all tests that need to run prior to pulling the trigger, Modernizr can then easily tell us when all tests have finished.

@mzedeler
mzedeler commented May 8, 2013

If Modernizr had a done call much like jQuery.Deferred() like this:

Modernizr.testDone('mytest', function(){
    ...
});

Then all that Modernizr would need to do when testDone is called is to check if the test has already been run, and either call the callback right away or - if the test hasn't run - defer the callback until after the test has finished.

Is it any more complicated than that?

@stucox
Member
stucox commented May 8, 2013

@mzedeler – that's essentially what we've decided on, although the working name is Modernizr.on().

See the PR here: #921
And polyfill for trying it out on Modernizr 2.x here: https://github.com/stucox/modernizr-on

@mzedeler
mzedeler commented May 9, 2013

Thanks for the polyfill. I'll start using it.

@ryanve
Contributor
ryanve commented May 14, 2013

Consider if .addTest could overwrite any falsey test (not just undefined ones). Then .addTest('example') would initialize as false and set the no- class. Later (via an event or timer) it could become truthy by calling .addTest('example', true) and have the no- class removed.

@SlexAxton
Member

The support of a browser should not change over time as far as I can think
of. I would imagine the only change can be from unknown to known.

@mzedeler

@ryanve: that behavior would be really confusing. When a test has returned false it means "this isn't supported", not "this may be supported - just try again later". It would be much more obvious to set up a hook that is called once the test has finished. Until then, undefined is much more valuable than a false negative (or positive for that matter) because you'd know for sure that the state is "maybe supported".

@stucox
Member
stucox commented May 14, 2013

One thought I did have is that it might be nice if we attached the property to the Modernizr object when the async test is declared (but leave it undefined) – i.e.:

<script src="modernizr.js"></script>
<script>
'datauri' in Modernizr  // true (with the current code this would be false)
Modernizr.datauri  // undefined
</script>

in case it's ever helpful to see which tests have been defined, without them having to have finished running.

Although that might mean changing the APIs (which may be no bad thing – I've mentioned before I think it's a little bit confusing):

Modernizr.addAsyncTest('datauri', function () {
   // ...
      resolveAsyncTest(true);
   // ...
});
@stucox
Member
stucox commented Jun 7, 2013

This was fixed by PR #921.

I've raised #969 to discuss my suggested API tweak above.

@stucox stucox closed this Jun 7, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment