One way data flow and yield

I was showing someone how yielding works with Ember components and got a pretty good question that stumped me. She said: “doesn’t that go against one-way data flow?”

How would one answer this question?

That’s a good question. I would try to explain it by analogy with higher-order functions in Javascript.

Functions can accept other functions as arguments. Here is a silly example:

function print123(how) {
  let numbers = [1,2,3];
  for (let number of numbers) {
    how(number);
  }
}

function toConsole(number) {
  console.log(number);
}

function toConsoleWithExclamation(number) {
  console.log(`${number}!`);
}

print123(toConsole);
print123(toConsoleWithExclamation);

And a convenient thing we do when calling higher-order functions is define their argument anonymously, inline:

function example1() {
  print123(number => {
    console.log(`${number}!`);
  });
}

Notice that console.log appears “inside” example1, but it’s not really part of example1 in the usual way. It’s still in a separate function, that just happens to be nested inside example1. print123 never calls example1, it calls the anonymous inline function.

Now, all of this is analogous with components. When a component invokes another component, that’s like a function calling another function. (This is not just an analogy, it’s really implemented as function invocation in the glimmer virtual machine that runs your templates.)

When you invoke a component and pass it a block:

{{! templates/component/example1.hbs }}
<PrintNumbers as |number|>
  <div>{{number}}</div>
</PrintNumbers>

The block is an anonymous function, just like in the Javascript case. Just as “console.log” appears inside the example1 function but is really not part of its own body, <div>{{number}}</div> appears inside the example1 component but it’s not really part of the component’s own body. It’s a separate block that will get called (zero or one or any number of times) by the PrintNumbers component.

So data never goes “up”. It goes down from example to PrintNumbers and then down again from PrintNumbers to the anonymous block when PrintNumbers says something like {{yield currentNumber}}.

2 Likes

Ah awesome. Thinking of blocks as anonymous functions makes a lot of sense. Thanks!