D3 version 5, how to chain promise to chart build

Viewed 337

I want to chain JS promise to D3 v5 chart building, and want to resolve it after the animation, but it's not working, the code below also show different types of attempts in the comments, none of them is working, option 3 cause exception (Uncaught (in promise) TypeError: Cannot read property 'on' of undefined), option 2 and option 1 just fail silently. Please advise, thanks heaps! PS: dataset TrumpStatsFiltered is correct, as I tested the code without the promise.

var dur = 400;
var t = d3.transition().duration(dur);

var durR = 400;
var tR = d3.transition().duration(durR).ease(d3.easeBounce).delay(220);

var swingState1 = d3.select('#svgSwingStateGraph1');

var swingState1Promise = () => {
    return new Promise((resolve, reject) => {
        swingState1
            .selectAll('text')
            .data([TrumpStatsFiltered], d => d.swing_state_1)
            .join(
                enter => {
                    enter
                    .append('text')
                    .attr('x', -150)
                    .attr('y', 30)
                    .text(d => d.swing_state_1 == 'Trump' ? "Trump":"Hillary")
                    .attr("stroke", d => d.swing_state_1 == 'Trump' ? "green":"orange")
                    .transition(t)
                    .attr('x', 75)
                    //.on("end", response => resolve(response)) //- option 2
                },

                update => {
                    update
                    .transition(t)
                    .text(d => d.swing_state_1 == 'Trump' ? "Trump":"Hillary")
                    .attr("stroke", d => d.swing_state_1 == 'Trump' ? "green":"orange")
                    //.on("end", response => resolve(response)) //- option 2
                },

                exit => {
                    exit
                    .transition(tR)
                    .attr("stroke", "tomato")
                    .attr('x', 200)
                    .remove()
                    //.on("end", response => resolve(response)) //- option 2
                }
            )//.on("end", response => resolve(response)) //- option 3

        swingState1.on("end", response => resolve(response)) // option 1
    })
}

Promise.all([swingState1Promise()]).then(() => {

    ***I need to hit this point!***
});
1 Answers

You can use transition.end() which

Returns a promise that resolves when every selected element finishes transitioning.

Because you have three selections—namely, the enter, update, and exit selections in .join()—you can create an array to collect those promises and pass it on to Promise.all() to await resolution of those promises when all corresponding transitions have come to an end. The following code shows how this could be done for the enter selection; obviously, the same pattern can be applied to collect promises from the update and the exit selection as well.

const transitions = [];                // Collect promises for enter, update, exit transitions.

someSelection.data(someData)
  .join(
    enter => enter.append("whatever")
      .attr("", "")                    // Modify some attributes.
      .call(enter => transitions.push( // Push the returned promise to the collector array.
        enter.transition()             // Create a new transition.
          .attr("", "")                // Attributes to be transitioned.
          .end()                       // Get the promise.
      ))
  );  

Promise.all(transitions)
  .then(() => {
    console.log("Done");               // This logs when all transitions have finished.
  });

Have a look at the following snippet for a working demo:

const svg = d3.select('body')
  .append("svg")
    .attr("width", 600)
    .attr("height", 200);

const transitions = [];

svg.selectAll(null)
  .data([500, 1500, 1000])
  .join(
    enter => enter.append('circle')
      .attr('cx', (_, i) => i * 50 + 100)
      .attr('cy', 100)
      .attr("r", 20)
      .attr("fill", "red")
      .call(enter => transitions.push(
        enter.transition()
          .duration(d => d)
          .attr('fill', "blue")
          .end()
      ))
  );  

Promise.all(transitions)
  .then(() => {
    console.log("Done");
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.15.0/d3.js"></script>

Related