zakandrewking / escher

Build, share, and embed visualizations of metabolic pathways.
https://escher.github.io
Other
210 stars 78 forks source link

Accessing the scope within callbacks #187

Closed matyasfodor closed 5 years ago

matyasfodor commented 7 years ago

I'm refactoring this code: https://github.com/DD-DeCaF/metabolica-ui-pathways/blob/master/src/pathways/escher.service.ts#L34

In this function I have to access both my EscherService's context and the Builder's context. The callback would normally access the builder scope with the this keyword, but that can't be used, since the context binding won't work with arrow functions. I could work it around but the solution is not very nice:

first_load_callback: ((escherService) => {
// This function is bound to the Builder ..
  return function () {
    // So this `this` references the non-lexical, Builder context
    return escherService.firstloadCallback(this);
  };
// This `this` references escherService
})(this)

I can propose two changes that could make this work nicely:

Creating the builder:

this.builder = escher.Builder(
    null,
    // ...
);

Accessing both contexts in the callback:

// ...
options = {
  first_load_callback: () => {
    // builder instance:
    this.builder 
    // EscherService context;
    this
  }

This is currently not possible, since the synchronous call. My my proposal is to wrap the call of the callbacks in a setTimeout or in promises.

What do you think about this issue? Do you think it is possible to mitigate this issue with one of these solutions?

zakandrewking commented 7 years ago

I see the problem. You're right: The first-load callback makes more sense behind a defer (setTimeout(fn, 0)).

However, I will probably keep the rest of the callbacks synchronous. They are used throughout Escher, and I'm not sure what effects would result from generally switching to asynchronous callbacks.

You could also try:

let builder;
builder = escher.Builder(
  ...,
  {
    first_load_callback: () => {
      console.log(builder);
    }
  }
);
matyasfodor commented 7 years ago

I just realized, using this callback is not even necessary, since the whole map creation is synchronous. I could just execute the function after the builder object was created.

zakandrewking commented 7 years ago

Just to have it here, I tried the method above with let and it did not work.

This is a general annoyance of using Builder within React or Angular where users will want references to both the Escher this and the React/Angular this.