8

I am trying to write code for the above described problem. I tried finding a solution. This is what I currently have.

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

var drawColorLine = function(start, end, color) {
  var deltaX, deltaY, i = 0,
    currLength = 0,
    isHor, isVert;

  deltaX = end[0] - start[0];
  deltaY = end[1] - start[1];
  context.strokeStyle = color;

  isHor = deltaX === 0 ? 0 : 1;
  isVert = deltaY === 0 ? 0 : 1;

  function draw() {
    context.beginPath();
    context.moveTo(start[0] + currLength * isHor, start[1] + currLength * isVert);

    currLength = currLength + 0.5 * i;
    context.lineTo(start[0] + currLength * isHor, start[1] + currLength * isVert);
    context.stroke();

    if (currLength <= Math.max(deltaX, deltaY)) {
      i = i + 1;
      requestAnimationFrame(draw);
    }
  }
  draw();
};

drawColorLine([40, 40], [100, 40], '#116699');
drawColorLine([40, 40], [40, 100], '#bb11dd');
<canvas id='canvas' width='400' height='400'></canvas>

The problem is both are being drawn at the same time. One should follow after the other. Using promsises is it possible to delay the second function while the first function is getting executed and later execute second function? I tried reading on Promises a bit but I couldn't translate what I understood into code.

Thanks in advance.

Ramana Venkata
  • 350
  • 8
  • 18

2 Answers2

5

Yes, you can use promises, altough for learning purposes you might want to write a pure callback solution first.

You'll want to have a look at my rules of thumb for promise development. Let's apply them here:

  1. Every asynchronous function must return a promise.

    These would be drawColorLine, draw, and requestAnimationFrame in your case.

  2. As requestAnimationFrame is a native, primitively asynchronous function that unfortunately still takes a callback, we'll have to promisify it:

    function getAnimationFrame() {
        return new Promise(function(resolve) {
            requestAnimationFrame(resolve); // this promise never gets rejected
            // TODO: cancellation support :-)
        });
    }
    
  3. Everything that follows an asynchronous action goes into a .then() callback:

    function drawColorLine(start, end, color) {
        … // initialisation
    
        function draw() {
            … // do work
            // always return a promise:
            if (/* furter work */) {
                i++;
                return getAnimationFrame().then(draw); // magic happens here :-)
            } else {
                return Promise.resolve(…); // maybe have a path object as eventual result?
                                           // or anything else, including nothing (no arg)
            }
        }
        return draw(); // returns a promise - but don't forget the `return`
    }
    

Voila!

drawColorLine([40, 40], [100, 40], '#116699').then(function() {
    return drawColorLine([40, 40], [40, 100], '#bb11dd');
}).then(console.log.bind(console, "both lines drawn"));
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I intially wrote a solution with callback. I had to draw a lot of lines. I want to do it sequentially. Callback didn't seemed the way to go or I couldn't find a nice way to extend my function. I tried reading promises from various places but I still don't have good idea how to use them effecitively. What is the way to learn re-read the stuff again? – Ramana Venkata Apr 28 '15 at 08:47
  • You can find lots of stuff at https://github.com/kriskowal/q/wiki/General-Promise-Resources – Bergi Apr 28 '15 at 11:05
  • Callbacks would work just as well, you'd just have to transform the rules a bit: "every async function must take a callback, and call it when it is finished", "everything after an async thing must go into its callback, and then call back in the end". It's passing continuations around whenever you would `return` a promise. – Bergi Apr 28 '15 at 11:07
  • Is it possible to chain promises recursively?? – Ramana Venkata Apr 28 '15 at 12:10
  • @RamanaVenkata: Yes, `then` callbacks can return promises which will be awaited (see [here](https://stackoverflow.com/a/22562045/1048572) or [here](http://stackoverflow.com/a/22000931/1048572)). My answer uses it in the `magic happens here` line :-) – Bergi Apr 28 '15 at 12:52
  • Bergi, not really the right place, but with a recursively-built promise chain you presumably get a call stack *and* a promise chain - ie "deep" and "wide". Has anyone considered the memory issues of doing this? If you think it worthwhile, I'll ask the question formally. – Roamer-1888 Apr 28 '15 at 13:22
  • @Bergi _"I have built my own promise library"_ Build own version of state machine https://www.promisejs.org/implementing/ ? , utilize native promises ? Available at large ? If yes, link to piece ? Did documentation , comments take as much or more review to compose as `js` ? – guest271314 Apr 28 '15 at 17:03
  • 1
    @guest271314: No, it's not ready to use at large (and has few to zero documentation), but you can [read its code here](https://github.com/bergus/F-promise) - if you are highly proficient with closures :-) – Bergi Apr 28 '15 at 17:03
  • @Bergi "algebraic" ? – guest271314 Apr 28 '15 at 17:19
  • 1
    @guest271314: [Algebraic](https://en.wikipedia.org/wiki/Algebraic_data_type) in the sense that you can construct promises for promises, and should care about the types of your callbacks. Unwrapping is only implicit for `then`. See also [this](https://github.com/fantasyland/fantasy-land) – Bergi Apr 28 '15 at 17:21
2

Try utilizing .animate() , jQuery.Deferred()

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

var drawColorLine = function(start, end, color) {
  // create new `deferred` object
  var dfd = new $.Deferred(),
    deltaX, deltaY, i = 0,
    currLength = 0,
    isHor, isVert,
    // create animation object
    props = $({"prop":i}); 

  deltaX = end[0] - start[0];
  deltaY = end[1] - start[1];
  context.strokeStyle = color;

  isHor = deltaX === 0 ? 0 : 1;
  isVert = deltaY === 0 ? 0 : 1;

  function draw(n) {
    context.beginPath();
    context.moveTo(start[0] + currLength * isHor
                   , start[1] + currLength * isVert);

    currLength = currLength + 0.5 * n;
    context.lineTo(start[0] + currLength * isHor
                   , start[1] + currLength * isVert);
    context.stroke();

    if (currLength <= Math.max(deltaX, deltaY)) {
      // create object to animate,
      // do animation stuff      
      props.animate({"prop":1}, {
        // set duration of animation
        duration:10, 
        complete:function() {
          // increment `n`:`i`
          n = n + 1;
          // call `draw` with `n` as parameter
          draw(n)
        }
      })         
    } else {
      // if `currLength > Math.max(deltaX, deltaY)`,
      // resolve `deferred` object, 
      // set `canvas` element as `this` at `.then()`
      // pass `deltaX`, `deltaY`, `currLength`, `n`` 
      // arguments to `.then()`
      dfd.resolveWith(canvas, [deltaX, deltaY, currLength, n]);
    }

    }
  draw(i);
  // return jQuery promise object
  return dfd.promise()
};
// draw first line
drawColorLine([40, 40], [100, 40], '#116699')
.then(function() {
  console.log("first line complete", arguments, this);
  // draw sencond line
  return drawColorLine([40, 40], [40, 100], '#bb11dd');
}).then(function() {
  console.log("second line complete", arguments, this);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<canvas id='canvas' width='400' height='400'></canvas>
guest271314
  • 1
  • 15
  • 104
  • 177