Re: [whatwg] HTMLCanvasElement toBlob Promise
On 3/18/15, acmesquares . acmesqua...@gmail.com wrote: Admittedly a wrapper function is trivial, mostly for API consistency, and does simply move the callback elsewhere. There is a lot of room for improvement for toBlob. However that's not unprecedented as Navigator.mediaDevices.getUserMedia() was just a promisification of an existing API. I did not know there was in-place promisification of gUM, but here it is:- http://w3c.github.io/mediacapture-main/getusermedia.html#dom-mediadevices-getusermedia The original non-promise version is still documented on MDN as of today:- https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getUserMedia Nor is the alternative since OfflineAudioContext.startRendering is being superseded by a Promise version in-place. They really went overboard there with promises on the new draft. I wonder what will break from those changes. My gut tells me two things 1) The canvas API (for toBlob, toDataURL) should have been fixed years ago: 2) don't break the web. I usually am pretty conservative about not breaking the web. But now that I'm looking at crumb camera code again, and that API, you're right (and Adria too). How much would break if toBlob is changed to return a promise? 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. What are you building? I used if-else and a callback function to work around the toDataURL/toBlob sync vs async paths. I'm not particularly proud of the code, but I will say that the quality of the toBlob/toDataURL API is reflected in it. In fact, I was not happy about it when I wrote it and I had other complaints about the canvas methods (still do). But because I wasn't an official member of the Nokia project, it would have been out of place for me to submit a bug re that, and potentially would have had ramifications, with her employer wondering about the extend of my help, or why I am filing a bug and providing examples of a project that I wasn't on. And she did not want to file a bug or provide feedback, though I offered to help with both of those. The problems with the canvas API should have been addressed then. But as we can see, it has been sitting there rotting with no feedback for over two and a half years, and apparently only Firefox implements it, is that right? Chromium Issue 67587: Add toBlob to Canvas element. https://code.google.com/p/chromium/issues/detail?id=67587 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() OK, so it seems intuitive to you to have it be designed with a promise. My gut tells me don't break the web by changing existing behavior. And I don't care about promises much. The toBlob algorithm has problems. If it is to be redesigned, it should address these problems: The formal parameters are missing, it accepts variable arguments, some those arguments are passed through. The pass through arguments are passed through as a variable ambiguously named named `arguments`, there is no type checking to see if the callback is callable, only to make sure that it is not `null`, in the middle of the algorithm, there is a step that says return, but continue, there are two additional parallel processes steps: Step 6 begins in parallel and step 7 kicks off a new task with Queue a task. There is an internal method named a serialisation of the canvas element's bitmap as a file for which no parameter variables are specified and in lieu of that, the note optionally with some given arguments, I can think of many ways this could be redesigned and respecified. https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob If you want to have a stab at a better redesign, I think that's a great idea. 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. That's useful to know. Always good to have an idea of what the consequences are before we go and break it. 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 ) Having a mix of sync/async is confusing. -- Garrett @xkit ChordCycles.com garretts.github.io personx.tumblr.com
Re: [whatwg] HTMLCanvasElement toBlob Promise
On 4/27/15, Garrett Smith dhtmlkitc...@gmail.com wrote: On 3/18/15, acmesquares . acmesqua...@gmail.com wrote: ... My gut tells me two things 1) The canvas API (for toBlob, toDataURL) should have been fixed years ago: 2) don't break the web. I usually am pretty conservative about not breaking the web. But now that I'm looking at crumb camera Coremob Camera, not crumb camera (the spellchecker got me). link: https://github.com/coremob/camera/commit/72f73c417725208dd5bf38e5a1b25b458ee7c424#diff-63cc4d41b7c8563893859fa3acdec6beR364 -- Garrett @xkit ChordCycles.com garretts.github.io personx.tumblr.com
Re: [whatwg] HTMLCanvasElement toBlob Promise
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 dhtmlkitc...@gmail.com wrote: On 3/17/15, Ashley Gullen ash...@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
Re: [whatwg] HTMLCanvasElement toBlob Promise
On 3/17/15, Ashley Gullen ash...@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
Re: [whatwg] HTMLCanvasElement toBlob Promise
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? I don't see any need to change toDataURL - it's already inefficiently designed by the fact it returns a data URL which will be bloated compared to a blob. So toBlob is naturally the preferred option for efficiency. On 17 March 2015 at 20:02, Garrett Smith dhtmlkitc...@gmail.com wrote: On 3/14/15, acmesquares . acmesqua...@gmail.com wrote: It would be great if there was a promise-based version of toBlob. Same parameters as toDataURL, but return a Promise to a blob. I use toBlob heavily with other promise APIs, and this one really stands out as in need of modernization. Hi Adria, toBlob: https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob ... The disparity between toDataURL - sync - and toBlob - async - can be awkward to handle. How would you write it as a promise? I don't agree that a promise would make that better; IMO it would just move the place of the callback. Maybe you can offer more insight on it. You can use a callback in the enclosing scope for both, as an adapter for toBlob. For example, (I helped anonymously get this working): - https://github.com/coremob/camera/blob/master/vanilla/js/main.js#L339 function getBlobFromCanvas(canvas, data, callback) { if (canvas.toBlob) { //canvas.blob() supported. Store blob. // (GS) This assignment is still a mistake; should result `undefined` var blob = canvas.toBlob(function(blob){ data.photo = blob; callback(data); }, 'image/jpeg'); } else { // get Base64 dataurl from canvas, then convert it to Blob var dataUrl = canvas.toDataURL('image/jpeg'); data.photo = util.dataUrlToBlob(dataUrl); if(data.photo == null) { console.log('Storing DataURL instead.'); isBlobSupported = false; data.photo = canvas.toDataURL('image/jpeg'); } callback(data); } } A comment on comments: I generally initial my code review comments so that once all the issues have been resolved, the resultant code has as few explanatory comments as needed (deliberate omission of copyright notice is tantamount to professional fraud). This strategy leads to asking if there are any comments left, and helps motivate the removal of comments which leads to authoring code that is self-explanatory and well-named. It also avoids misleading comments, or situations where the code comments fall out of sync with what the code is actually doing, such as in the example above:- //canvas.blob() supported. Store blob. This code comment review practice can be replaced by repo commit comments (stash/gitlab/github), but comments work great in email or other mediums such as this (despite contradicting articles http://www.thinkful.com/learn/javascript-best-practices-1/#Comment-as-Much-as-Needed-but-Not-More ) But to get back to your `toBlob` promise idea, would you want it as a new method like `getBlob(mimeType, quality)`? But then what about toDataURL? Would you have that as a promise, too, or would you leave it alone? canvas.getBlob(mimeType, quality).then(callback, errback); ~ vs ~ var dataUrl = canvas.getDataURL(mimeType, quality); util.dataUrlToBlob(dataUrl, callback, errback); ? -- Garrett @xkit ChordCycles.com garretts.github.io personx.tumblr.com
Re: [whatwg] HTMLCanvasElement toBlob Promise
On 3/14/15, acmesquares . acmesqua...@gmail.com wrote: It would be great if there was a promise-based version of toBlob. Same parameters as toDataURL, but return a Promise to a blob. I use toBlob heavily with other promise APIs, and this one really stands out as in need of modernization. Hi Adria, toBlob: https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob ... The disparity between toDataURL - sync - and toBlob - async - can be awkward to handle. How would you write it as a promise? I don't agree that a promise would make that better; IMO it would just move the place of the callback. Maybe you can offer more insight on it. You can use a callback in the enclosing scope for both, as an adapter for toBlob. For example, (I helped anonymously get this working): - https://github.com/coremob/camera/blob/master/vanilla/js/main.js#L339 function getBlobFromCanvas(canvas, data, callback) { if (canvas.toBlob) { //canvas.blob() supported. Store blob. // (GS) This assignment is still a mistake; should result `undefined` var blob = canvas.toBlob(function(blob){ data.photo = blob; callback(data); }, 'image/jpeg'); } else { // get Base64 dataurl from canvas, then convert it to Blob var dataUrl = canvas.toDataURL('image/jpeg'); data.photo = util.dataUrlToBlob(dataUrl); if(data.photo == null) { console.log('Storing DataURL instead.'); isBlobSupported = false; data.photo = canvas.toDataURL('image/jpeg'); } callback(data); } } A comment on comments: I generally initial my code review comments so that once all the issues have been resolved, the resultant code has as few explanatory comments as needed (deliberate omission of copyright notice is tantamount to professional fraud). This strategy leads to asking if there are any comments left, and helps motivate the removal of comments which leads to authoring code that is self-explanatory and well-named. It also avoids misleading comments, or situations where the code comments fall out of sync with what the code is actually doing, such as in the example above:- //canvas.blob() supported. Store blob. This code comment review practice can be replaced by repo commit comments (stash/gitlab/github), but comments work great in email or other mediums such as this (despite contradicting articles http://www.thinkful.com/learn/javascript-best-practices-1/#Comment-as-Much-as-Needed-but-Not-More ) But to get back to your `toBlob` promise idea, would you want it as a new method like `getBlob(mimeType, quality)`? But then what about toDataURL? Would you have that as a promise, too, or would you leave it alone? canvas.getBlob(mimeType, quality).then(callback, errback); ~ vs ~ var dataUrl = canvas.getDataURL(mimeType, quality); util.dataUrlToBlob(dataUrl, callback, errback); ? -- Garrett @xkit ChordCycles.com garretts.github.io personx.tumblr.com