Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • May 31 02:27
    brianmhunt synchronize #136
  • May 31 02:27

    brianmhunt on 130-1-package-update

    ci) disable CI altogether until… (compare)

  • May 31 01:46

    brianmhunt on 130-1-package-update

    ci) switch back to SauceLabs w/… (compare)

  • May 31 01:37
    brianmhunt commented #134
  • May 30 22:13
    brianmhunt synchronize #136
  • May 30 22:13

    brianmhunt on 130-1-package-update

    ci) try GabrielBB/xvfb-action t… (compare)

  • May 30 22:01
    brianmhunt synchronize #136
  • May 30 22:01

    brianmhunt on 130-1-package-update

    ci) install electron / npm (compare)

  • May 30 21:57
    brianmhunt synchronize #136
  • May 30 21:57

    brianmhunt on 130-1-package-update

    ci) try xvfb-run on Github acti… (compare)

  • May 30 13:18
    brianmhunt synchronize #136
  • May 30 13:18

    brianmhunt on 130-1-package-update

    ci) disable SL on pull; set tes… (compare)

  • May 30 13:16
    brianmhunt synchronize #136
  • May 30 13:16

    brianmhunt on 130-1-package-update

    ci) disable SauceLabs, enable l… (compare)

  • May 30 12:04
    brianmhunt synchronize #136
  • May 30 12:04

    brianmhunt on 130-1-package-update

    ci) disable Safari as it always… (compare)

  • May 30 11:55
    brianmhunt synchronize #136
  • May 30 11:55

    brianmhunt on 130-1-package-update

    ci) disable startConnect by def… (compare)

  • May 30 02:53
    brianmhunt synchronize #136
  • May 30 02:53

    brianmhunt on 130-1-package-update

    ci) remove package-lock.json du… (compare)

Ken Hadden
@cosmoKenney
Thanks @avickers.
Micah Zoltu
@MicahZoltu
What is the status on TKO and async integration?
The number one problem I run into with KO continues to be async code. I have an asyncComputed, but the problem runs deeper than just that.
Functions like subscribe() don't natively understand async functions and if I give it an async function and that function throws the error will be lost.
In general, I just find myself having to jump through a lot of hoops and write a lot of very careful code in order to use KO with async heavy libraries (which is basically everything I use these days).
I'm hoping that TKO will resolve this, and I wanted to check in to see if that is something that has already happened, will happen, or will not happen so I can decide how to proceed with new projects.
Micah Zoltu
@MicahZoltu
Another example is that I frequently have click/submit handlers that execute async code. If my model has async foo() { ... } and I use it as a KO click/submit handler, any error it throws will get lost.
Andrew Vickers
@avickers

@MicahZoltu I’m not sure how Brian is handling this. I have been working on a library of my own to modernize Knockout called Knockdown.

I will tell you what I did, and maybe it will be useful for TKO or maybe not.

I moved away from expressions into HTML to chaining in JS. Observable has two methods to support async .await() and .defer()

myProp: ko.observable().await(Promise)
Executes the promise-returning expression as soon as applyBindings is called.

myDeferred: ko.observable().defer(Lambda expression)
Deferred only executes the lambda after Promise.all() for all the awaits.

An initial default value can be supplied.

So await is for "above the fold" content and interactive UI components, and defer is for anything else.

This two tier approach is helpful for performance.

Micah Zoltu
@MicahZoltu
@avickers IIUC, Hmm, I don't quite follow. Can you give an example of how you would use .await and .defer?
async function apple() { await delay(500); return 5 }
ko.observable().await(apple())
ko.observable().defer(apple)
What happens in each case?
Brian M Hunt
@brianmhunt
For some async improvement, in TKO I've added when, yet, once and next (at https://github.com/knockout/tko/blob/master/packages/observable/src/subscribable.js#L127-L157) — basically looks like this:
Casey Webb
@caseyWebb
Promise-aware computeds would be a game-changer, although I have no idea what it would entail to implement
Brian M Hunt
@brianmhunt
>>> x = ko.observable()
>>> x.yet(undefined).then(console.log)
>>> x.when(v => v === true).then(console.log)
>>> x.once(console.log)
>>> x.next().then(console.log)

>>> x('abc')
123
123
123
>>> x(true)
true
@caseyWebb Yeah, I've given that a lot of thought, and there's basically a couple re-entrance problems that need to be sorted... it might end up looking something like this:
ko.computed(async () => {
   /// synchronous code
   ko.chainAwaitPromise(await some_async_thing)
   /// more code added to the computed dependency chain
})
... that said, there's lots of bizarre problems that can occur.
Casey Webb
@caseyWebb
I didn't even consider the implications of having to add things to the watched dependencies in awaited calls; I was mostly thinking the difficulties involved in dealing with race conditions and then the tricky question of should it be handled by the user or in the library
it's also not like it's hard to implement on consumer side with multiple observable. I'll add a subscription with the async handler to the obs that triggers a recompute, and then set another obs with the awaited value at the end of the func
Brian M Hunt
@brianmhunt
Here's some very old code that I haven't tested in ages, but might be good food for thought
//
// resolveOn{Write|Read}
// ---
// Extenders to help deal with promises and their resulting values.
//
// Use resolveOnWrite when you want the target observable to store
// the values.
//
// Use resolveOnRead when you want the target observable to store the
// promise.
//
// If a ko.computed returns a Promise, you can automatically resolve it
// with resolveOnRead.
//

function no_op() {}


//
// resolveOnWrite
//      extender
// ----
// Any values that are written are resolved before writing.
//
// Successive writes will negate any prior unfulfilled promises that had
// been written.
//
// Rejections are passed to on_error, if it is a function.
//
// All writes to the target are now asynchronous, pending resolution of the
// (Promise.resolve or given) promise.
function resolveOnWrite(target, on_error) {
  var latest_promise
  on_error = typeof on_error === 'function' ? on_error : no_op

  return ko.computed({
    pure: ko.isPureComputed(target),
    write: (v) => {
      var this_promise
      this_promise = latest_promise = Promise.resolve(v)
        .then((resolved_value) => {
          if (latest_promise === this_promise) {
            target(resolved_value)
          }
        })
        .catch(on_error)
    },
    read: target
  })
}


// resolveOnRead
// ---
//
// Anytime target changes, resolve its promise-value first and return
// what is resolved.
//
// Same rules as resolveOnWrite for successive writes and rejections.
//
// Reads from the computed created here are asynchronous.
//
function resolveOnRead(target, on_error) {
  var published_value = ko.observable()
  var last_target_value
  on_error = typeof on_error === 'function' ? on_error : no_op

  return ko.computed({
    pure: ko.isPureComputed(target),
    write: target,
    read: () => {
      var this_target_value = target()
      if (this_target_value !== last_target_value) {
        // The target value has changed, so we will want to update the
        // value we publish to the observers.
        last_target_value = this_target_value
        Promise.resolve(last_target_value)
          .then((v) => {
            // Make sure we are resolving for the most recent target value.
            if (last_target_value === this_target_value &&
                v !== published_value()) {
              published_value(v)
            }
          })
          .catch(on_error)
      }
      return published_value()
    }
  })
}
I'm not suggesting that go into TKO or any other library 😀... but it covers a few of the bases of something that might.
Micah Zoltu
@MicahZoltu
It is certainly possible to work around the total lack of async/await support in KO 3, and that is in fact what I'm doing right now. I just find myself writing a ton of boilerplate to make it work.
This has lead me to start evaluating alternative frameworks that integrate better with async/await because I'm exhausted constantly having to do integration layers between all of my view-model stuff and all of my library stuff (which is almost entirely async/await).
There are a few different patterns I follow throughout my code, and I have wrapper functions to minimize the amount of effort I have to do each time I need to implement one of these patterns, but it ends up being error prone because it is so easy to screw up.
Andrew Vickers
@avickers

@MicahZoltu Was Brian able to help you?

I don’t want to drag this channel off topic. A quick example would be,

this.vm = {
      viewA11y: ko.observable().await(import('./views/a11y')),
      viewRepo: ko.observable().onIntersect(() => import('./views/repo')),
      demo: ko.observable().defer(() => import(‘./views/demo’))
}

These are all html bindings, and the ‘views' are all web components/custom elements.

The A11y view begins loading immediately because of .await().

The Repo view will not begin to load until the binding intersects with the viewport (plus, by default, a margin of 50% of viewport height).

The Demo view will not begin loading until after A11y and every other .await()’d observable have resolved.

This helps improve Time To Interactive; helps reduce main thread load upon initial navigation; and, helps mitigate race conditions. If Demo depended upon something from A11y for instance, you would be sure that A11y would have loaded and initialized before Demo began loading.

Andrew Vickers
@avickers
@brianmhunt
Are Observables ‘thenables' under TKO? Such that, if a .when() or .yet() is provided, they await a passing condition until they ‘resolve' and their subscriptions bubble? or am I misunderstanding?
Andrew Vickers
@avickers
Oh, and I approached computeds like deferreds. They are first evaluated only after all of the initial async (awaits) are finished. I have found the two tier approach pretty helpful for mitigating race conditions.
Micah Zoltu
@MicahZoltu
@avickers In your example, why would demo wait until viewA11y or viewRepo are loaded? I do not see any dependency between them.
Also, what do you mean by "binding intersects with viewport"?
Andrew Vickers
@avickers
Observable.onIntersect() uses the IntersectionObserver API to determine when to load the content and process the binding.
So when an observable has .onIntersect, it gets added to the IntersectionObserver. When a div that is bound to that Observable gets close to scrolling into view, the onIntersect expression is called, calculating/loading the value.
Andrew Vickers
@avickers

Observable.await() and .defer() just provide two tiers of async. Await begins immediately when applyBindings() is called and iterates through the View Model props. Defer gets put into a queue. Deferred/Computed properties do not begin to process until after all sync and await()’d props have been resolved. The two tier approach is just to mitigate race conditions, and to allow developers to prioritize resources that are either in the viewport initially and/or interactive.

Defer()’d observables do not wait for onIntersect(), however; onIntersect() is conditioned on user behavior.

Micah Zoltu
@MicahZoltu
@avickers Ah, it sounds like these things are specifically for async page loading, not async JS function calls (I use the latter, almost never the former).
For example, a pureComputed that takes an async function as its parameter.
Or a subscription that takes an async function.
Andrew Vickers
@avickers

Well, the library doesn’t know or care what the async functions are for as long as they return a promise.

You could await() ajax/database lookups/serverless functions and a computed would wait until they have resolved to evaluate.

I suppose you could accomplish this with traditional KO a little less gracefully. You could add all of your initial async functions to Promise.all().
Promise.all(theAsyncThings).then(() => ko.applyBindings(vm))
Micah Zoltu
@MicahZoltu

You could await() ajax/database lookups/serverless functions and a computed would wait until they have resolved to evaluate.

This isn't true in KO, is this a new feature in TKO that it accepts Promise returning functions and will wait until they resolve before they update the observable?

Casey Webb
@caseyWebb
I believe @avickers is referring to his fork, knockdown
Andrew Vickers
@avickers
Correct. I’d like to add that functionality as a plugin to TKO some day, but I need to get a better grasp of Lerna, Rollup, and the TKO plugin/build system first.
Casey Webb
@caseyWebb
tko build system is/was in flux too, good idea to wait. I've got a not-quite-abandoned-but-sidelined PR for Typescriptifying tko
Brian M Hunt
@brianmhunt
@avickers Sorry for the delay — Yes, obs.when() and .yet are Promises (https://github.com/knockout/tko/blob/master/packages/observable/src/subscribable.js#L141). @caseyWebb I've learnt a lot of TKO so I might be in a position to actually contribute to the Typescriptification now :)
Brett Ryan
@brettryan
Hello all. I've just been made aware of tko having been using knockout for a long time now. What is the state of tko? Would love to donate if it's alive and well.
Flavius Demian
@slown1
great question Brett
it is important to know about the feature
Adalyat Nazirov
@AdalyatNazirov
hi there
is there any plans for Knockout 4.0 release date?
Nuno Cruz
@nmocruz
Last year the project was almost stoped
Adalyat Nazirov
@AdalyatNazirov
oh. sad
Brian M Hunt
@brianmhunt
Hi all, TKO's very much alive and a core part of the company where I work (MinuteBox). The good news is that there are basically no known issues (and we honestly haven't seen anything significant for months), but I don't have the bandwidth right now to support a release cycle or come up with a timeline, but I'm optimistic that it's going to be something we can release in 2021. 🤞 Whether Knockout 4.0 is based on TKO is a slightly different analysis, a bit more of a community question involving the other members of Knockout (Michael Best, Ryan Niemeyer, Steve Sanderson). Sorry I can't offer something more concrete right now, but rest assured that TKO's not abandoned.
modeler42
@modeler42
Hi, do you have any hints how to get the last alpha versions run on IE 11? Tried the Knockout 4 - Builds, but they seem to contain some ES6 or require some undocumented polyfills.