Coder Social home page Coder Social logo

Losing scope in async global effects about spect HOT 9 CLOSED

dy avatar dy commented on July 28, 2024
Losing scope in async global effects

from spect.

Comments (9)

dy avatar dy commented on July 28, 2024

(getting dejavus with implementing jquery-spect, exactly the thing.)

Seems that we're limited in hooks to repeat react. There's no known for me way for out-of-scope function to detect the scope (in general case) it was called from, so the effects are quite limited in use-cases, quite like react.

Switching state manually isn't useful for async calls either.

Seems that we're off with jquery-spect only, providing aspect builder plugin, and pure no-jquery version.

from spect.

dy avatar dy commented on July 28, 2024

jQuery code is confusing and noisy, although promising for jquery land: https://gist.github.com/dy/a556d817042c3d492455e93831fd3572.

Within all options, scope detection is the fastest and the safest.
We can hoist up looking for the first matching effect, to avoid fn.toString.
Static transform would provide robust solution.

from spect.

dy avatar dy commented on July 28, 2024

Ugh. That doesn't seem to be possible via stacktrace.

let log = [],
    el = document.createElement('div'),
    el2 = document.createElement('div')
  
  // invocation site - available from stack
  $(el, aspect)

  // the definition site (start of aspect) - unavailable from stack
  function aspect(el) {
    log.push(state().x)

    $(el2, el2 => {});

    (
      () => {
        $(el2, el2 => {});
        setTimeout(() => {
          $(el2, el2 => {});
          // the timeout site (topmost detectable) 
          (() => state({ x: 1 }))()
        })
      }
    )()
  }

Neither creating new function helps detecting aspect - we can get only site of function evaluator, which says nothing about exact aspect.

The new functions know nothing about imported deps, they need providing effects as arguments anyways.
So we're off with runtime sandboxing only via passing fx as args.

from spect.

dy avatar dy commented on July 28, 2024

Ok. Even if we try to create scoped function via scoped-function, we lose function surroundings, like imported modules. To use scoped functions, we have to indicate scope.

So seems that's proven - there's no way to provide context-less global effects directly, not changing their API (useState-like, creating local reference) or keeping reference (jQuery).

from spect.

dy avatar dy commented on July 28, 2024

Afterthought

We could use state from timeout effect as

function app(el) {
  timeout(() => {
    state()
  })
}

In this case timeout would enable aspect before triggering callback.

There's limitation for fx though:

function app(el) {
  fx(async fn() {
    await smth()
    state()
  })
}

The async tick is guaranteed, and for state there's no way to identify target.

Why doubts in jQuery approach?

  • Because fx can be a direct effect, there's no necessity to keep it attached to holder.
  • Because route must be a global effect, there's no sense to keep it per-element.
  • Because html is first-class anonymous html constructor.

We could, actually, attach effects explicitly via deps, so that we wouldn't have to register implicit observables via reading some external state.

function app(el) {
fx(async (state, attr, id) => {
await tick()
// state is attached to current element context
state({ el: value})
}, state, attr, id)
}

That looks weird though - passing context along with attribs.

On the other side

  • Domain connectors $el.html = code look way more natural
  • They can act as holders of effects, not necessary to create global imports.

from spect.

dy avatar dy commented on July 28, 2024

Although - state seems to be the only bottleneck.
If we redesign it as

let state = useState(defaults)
//...
setTimeout(()=>{
state.x = 1
})

from spect.

dy avatar dy commented on July 28, 2024

Getting back to this lovely approach.
jQuery-refs way has a set of disadvantages, most of all, confusing VDOM construction and $ mess: #80+.
If we actually provide all possible async effects #66 (comment), #67, we can go on with this approach. Global effects are valuable piece.

The only issue remains then - how to apply context.

use.call(target, el => {
   state.id = 1

  fx(() => {
    attr(a => a.loading = true)(el)
    attr.loading = true
    state.user = await ky.get`./api/user/${ state('id') }`
    attr.loading = false
  }, [id])

  html`<p use=${i18n}>${
    attr.loading ? `Hello, ${ state.user.name }!` : `Thanks for patience...`
  }</p>`

  use(() => {
    // like that probably?
    // but how do we switch context back? eg. read initial state?
    // mb contexts are not for that? eg. use(anotherAspect)
    // or manual indeed? ctxStart(), ctxEnd()?
  }, externalEl)

  css`...`
})

Although even getContext('2d') is basically an extension of element functionality. In other words we do

let elCtx = el.getContext('spect')
// or
let $el = context(el)
// or
let $el = spect(el)

So we just build a platform of contexts.

from spect.

dy avatar dy commented on July 28, 2024

Couple more reasons against jquery-refs. Yes, we add chaining, but.

  • we have to create multiple wrappers.
  • we can't easily store global state for example.
  • we can't easily wrap any target
  • it creates collection/single instance confusion
  • having effect(target, params) would be just natural atomic pattern, allowing to wrap anything
  • having effect(target) would enable FP style naturally
  • there's still conflict importing global effects vs getting them via local instance
  • there's conflict with real jQuery
  • for every effect in jquery we anyways call $target.fx(params) - not much effort is saved vs fx(target, params) - but the whole concept of wrappers is introduced, instead of centralized handling elements/collections without external stuff
  • spect wrapper is unfortunately ugly-ish in console
  • direct effects resolves issue of components
html(target, h`${ data.map(tpl) }`)
const tpl = data => h`...`

html(target, h`${ data.map(item => h`<${Comp} ...${item}/>`) }`)
const comp = el => html(el)`<${}/>`
  • that is lovely and natural to change(target, how)

from spect.

dy avatar dy commented on July 28, 2024

Ok, that's a separate ticket.

from spect.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.