26

I'm working with HTMLCanvas element that return the blob object outside of the async toBlob() function. This function doesn't return an output value, so I'm trying to declare a variable outside and access it through the command.

How I can use JS Promise for this scenario?

var myblob;
            canvas.toBlob(function(blob) {                         
                              myblob = blob;
                              console.log("inside " + myblob); // getting value after the console outside
                           })
 console.log( "outside " + myblob); // getting undefined   
Alexan
  • 8,165
  • 14
  • 74
  • 101
Sowmyan Soman
  • 853
  • 1
  • 9
  • 21
  • Perform the task within `.toBlob()` callback or use `Promise` constructor – guest271314 Feb 25 '17 at 17:01
  • 2
    Possible duplicate of [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](http://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) – Andreas Feb 25 '17 at 17:05

4 Answers4

69
const blob = await new Promise(resolve => canvasElem.toBlob(resolve));
KeyKi
  • 2,693
  • 3
  • 12
  • 24
26

You can use Promise constructor, pass Blob instance to resolve(), access Promise value at .then()

function getCanvasBlob(canvas) {
  return new Promise(function(resolve, reject) {
    canvas.toBlob(function(blob) {
      resolve(blob)
    })
  })
}

var canvasBlob = getCanvasBlob(canvas);

canvasBlob.then(function(blob) {
  // do stuff with blob
}, function(err) {
  console.log(err)
});
guest271314
  • 1
  • 15
  • 104
  • 177
  • Thank you so much and apreciate your prompt response. My issue, is I have to set the value of blob in a global variable so that I can access it outside the this function. eg: inside the canvasBlob.then(function(blob)){ this.something = blob; } – Sowmyan Soman Feb 25 '17 at 19:04
  • You will not be able to access `Blob` until `.toBlob()` callback returns a value. Use first function passed to `.then()` to perform task with `Blob` from `.toBlob()` callback. – guest271314 Feb 25 '17 at 19:07
  • Thanks. In my scenario, I have a video element and canvas to capture a snapshot from the video and render it in an image control. Currently the code executes in the capturing mouse click event. Now, once I have the picture I have to let the user to click on a button send that picture to the server via ajax request. Currently toBlob() is executing in the capture event, i would like to see if there is way to get that blob stored so that i can access it in the button click event to submit it to the server. Any advise would be very helpful ! – Sowmyan Soman Feb 25 '17 at 19:24
  • I think by calling the .toblob() second time in the button click returns the same image. Thank you ! – Sowmyan Soman Feb 25 '17 at 19:44
  • Your initial attempt is close. When `.toBlob()` is called, assign `myblob` the value of `Blob` at `.toBlob()` callback. See also http://stackoverflow.com/questions/42234835/how-to-take-a-photo-of-an-img-video-stream-html5-javascript/ – guest271314 Feb 25 '17 at 19:44
4

Typescript version:

function toBlob(canvas: HTMLCanvasElement): Promise<Blob> {
  return new Promise((resolve) => {
    canvas.toBlob(blob => {
      if (blob) resolve(blob);
    });
  });
}
rofrol
  • 14,438
  • 7
  • 79
  • 77
3

I HAVE ANOTHER IDEA... similar to the one marked as best idea... the problem with then is that you have to be inside the block in order to pick up the result from the promise. Now Javascript supports await similar to c#, this is good because you can run the function and wait until is resolved, then you get the result to a variable not attached to any function, example:

/* basically we create a function that receives a canvas called mycanvas, then we tell the function to return a new Promise, this promise is "resolved" inside the .toBlob method which by default provides a Blob parameter. */

function getCanvasBlob(mycanvas) {
  return new Promise(function(resolve, reject) {
    mycanvas.toBlob((blob) => {
      resolve(blob)
    })
  })
}

var myblob;

try{
  myblob = await getCanvasBlob(CANVAS_FROM_SOMEWHERE);
}
catch (error){
  console.log(error);
}

/* There's no need to use try/catch, you could use it just like this */

var myblob = await getCanvasBlob(CANVAS_FROM_SOMEWHERE);

As the method is async, then() is not executed or called until the promise is resolved, anything outside this method will not be notified of changes until is too late... The advantage here is that our javascript will not continue until the promise has been fully resolved.

ADDITIONAL INFO: If you call await, the method from where you are calling this function must be marked as async, otherwise you will see an error... lets say for example this is called inside a click of a button:

/* OBVIOUSLY this will throw an error because I don't have a real canvas...*/

$( document ).ready(function() {
    
    $('#mybutton').bind('click', async function(){
    
      //...more code...
      //...more code... until...
      
      var CANVAS_FROM_SOMEWHERE;
      
      var myblob = await getCanvasBlob(CANVAS_FROM_SOMEWHERE);
    
    });
    
});

function getCanvasBlob(mycanvas) {
  return new Promise(function(resolve, reject) {
    mycanvas.toBlob((blob) => {
      resolve(blob)
    })
  })
}

   

    
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="mybutton">process my canvas!</button>
Guillermo Perez
  • 589
  • 4
  • 10