thatOneGuy
thatOneGuy

Reputation: 10612

Using asynchronous call when setting text D3

I have some code similar to the following :

nodeEnter.append('text')
  .text(async function(d) {
    var count = await dispatch('GetCount', {
      chosenNode: d
    });
    return count || 'N/A';
  });

When running this, the text that shows looks like this :

[object Promise]

The function works but obviously returns before the promise returns anything. How would I go about awaiting an action in code similar to the above ?

I am using Vuex with VueJs, that's what the dispatch is working with.

Upvotes: 4

Views: 564

Answers (1)

Richard Matsen
Richard Matsen

Reputation: 23483

The d3 .text() method does not play well with async / await.

The promise object you see is because async function()... is returning a promise. Even if you just return a constant from an async decorated function, you still get a promise sent to the d3 text() method.

Here is the source of d3's text() method

function textRemove() {
  this.textContent = "";
}

function textConstant(value) {
  return function() {
    this.textContent = value;
  };
}

function textFunction(value) {
  return function() {
    var v = value.apply(this, arguments);
    this.textContent = v == null ? "" : v;
  };
}

export default function(value) {
  return arguments.length
      ? this.each(value == null
          ? textRemove : (typeof value === "function"
          ? textFunction
          : textConstant)(value))
      : this.node().textContent;
}

Luckily, when a function is passed it is invoked with the apply() method which binds the d3 'this' context, so we can easily carry out the textContent assignment within the .then() callback of the promise, like so

/* 
  Because the 'this' context changes below, need to grab 'dispatch' here
  This is specific to Vuex implementation mentioned in the question
  When using a different async function, just make sure it's within closure
*/
const dispatch = this.$store.dispatch  

nodeEnter.append('text')
  .text(function(d) {
    dispatch('GetCount', {
      chosenNode: d
    }).then(val => {
      this.textContent = val  // This is normally done in d3's text() method
    })

    /* 
      Don't return anything.
      d3's text() method will receive 'undefined'
      and not update textContent
    */
    // return count || 'N/A';
  });

Upvotes: 3

Related Questions