W3C home > Mailing lists > Public > whatwg@whatwg.org > March 2015

Re: [whatwg] HTMLCanvasElement toBlob Promise

From: acmesquares . <acmesquares@gmail.com>
Date: Wed, 18 Mar 2015 07:42:34 +0000
Message-ID: <CAONTYmjwrXACGXLyozaY-f=50Xw8Thm3hHqd7p=j09igseXsZA@mail.gmail.com>
To: "whatwg@whatwg.org" <whatwg@whatwg.org>
Admittedly a wrapper function is trivial, mostly for API consistency, and
does simply move the callback elsewhere.

However that's not unprecedented as Navigator.mediaDevices.getUserMedia()
was just a promisification of an existing API.

Nor is the alternative since OfflineAudioContext.startRendering is being
superseded by a Promise version in-place.


 Personally I monkey patched a new method toBlobPromise, and use that to
the total exclusion of the original. It's unavoidable when using
Promise.all() anyway.

Actually on initial encounter, I'd be least surprised if toBlob did return
a 'promise which resolves with a Blob'. Just like Fetch API Body.blob()


 Regarding existing implementations, Mozilla implemented in core in Feb
2013, and moved processing off main-thread Feb 2014. Webkit deferred to
Chromium which never finished implementation, so usage in the wild will
have been heavily impeded, and would necessarily fall back on toDataURL.
Jankiness is the least concern given this function can easily crash the
context.

Since the spec is converging on Canvas-in-Worker (transferable ImageData,
DOM independent CanvasRenderingContext2D etc), I'd also consider how to get
my Blob image representation inside a Worker.


 I probably shouldn't have dragged toDataURL into this, but in response I'd
avoid using it. A function which returns either dataURL or Blob is useless
as no other API speaks dataURL. If forced for legacy reasons I'd probably
convert using FileReaderSync.readAsDataURL( blob )

So yes, I'd leave that synchronous API alone.

 ~adria


On 18 March 2015 at 06:04, Garrett Smith <dhtmlkitchen@gmail.com> wrote:

> On 3/17/15, Ashley Gullen <ashley@scirra.com> wrote:
> > Making toBlob return a promise is definitely in keeping with the rest of
> > the web platform, but it's easy to write a wrapper function to promisify
> > it. IMO toBlob is better than toDataURL since it is async so the image
> > encoding can happen off main thread, reducing jank. I don't know how easy
> > it is for existing implementations to change - is it easy to return a
> > promise if no callback is provided?
> >
>
> Do you want the promise to provide an error handler or is this just
> for style consistency?
>
> // current API
> canvas.toBlob(callback, type, options);
>
> // Promise with & without error handler
> canvas.toBlob(type, options).then(callback, errback);
> canvas.toBlob(type, options).then(callback)
> canvas.toBlob(type).then(callback);
> canvas.toBlobl().then(callback);
>
> You could write a wrapper, but I think it's not as simple as you think it
> is.
>
> Optional middle argument overloading adds complexity. And the
> complexity of adding that complexity is propagated to anyone using
> that API. Because then, if they wanted to get a Promise from toBlob,
> they would first need to feature test the environment see if
> `canvas.toBlob()` with no arguments returns a promise.
>
> Then, after you've feature tested, you'll need to branch to handle the
> environments which aren't up to speed with the new promise-returning
> canvas.toBlob(). This is going to get into the complexity of handling
> added complexity in a dynamic deployment environment, and it will also
> entail writing a Promise adapter for the fallbacks to the proposal
> will need.
>
> Promise support is spotty and ECMAScript 6 isn't done. So you'll need
> a fallback for the getBlob adapter branch that handles the
> environments where toBlob doesn't return a promise and the environment
> doesn't support Promises.
>
> var toBlobSupportsPromise = function() {
>   var x = document.createElement("canvas");
>   try {
>     x = x.toBlob();
>     return''+x=="[object Promise]";
>   } catch(ex) {
>     return false;
>   }
> }();
>
> A wrapper (untested):
>
> function getBlob(canvas) {
>   if (toBlobSupportsPromise) {
>     getBlob = function getBlob(canvas) {
>       return canvas.toBlob();
>     };
>   } else {
>     getBlob = function(canvas) {
>       return new Promise(function(resolve, reject) {
>         canvas.toBlob(function(blob) {
>             resolve(blob);
>         });
>       });
>     };
>   }
>   var ret = getBlob(canvas);
>   canvas = null;
>   return ret;
> }
>
> But then you'll need to add support for Promises, where that isn't
> supported. Rewriting a new javascript library sounds like a terrible
> idea.
>
> Instead of trying to change toBlob, how about a different-named
> function, like `getBlobPromise`? Give browser vendors time and
> discourage libraries from trying to write more wrappers, adapters,
> polyfills, etc.
>
> Overloading with optional middle arguments creates more problems (like
> Dart, Polymer, Angular, etc).
>
> I can definitely understand the lure and attraction of getting sucked
> into a promise -- believe me on that!  But when there are too many
> things riding on and coupled to it, well, it gets complicated.
>
> Me, I'm a simple guy. I'm fine with callbacks. But I don't mind
> exploring these API design ideas.
> --
> Garrett
> @xkit
> ChordCycles.com
> garretts.github.io
> personx.tumblr.com
>
Received on Wednesday, 18 March 2015 07:42:59 UTC

This archive was generated by hypermail 2.4.0 : Wednesday, 22 January 2020 17:00:29 UTC