Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Nov 06 15:27

    brianmhunt on master

    lifecylle) Expand `LifeCycle::a… LifeCycle) Re-anchor on multipl… (compare)

  • Oct 31 20:41
    dependabot[bot] labeled #107
  • Oct 31 20:41
    dependabot[bot] opened #107
  • Oct 31 20:41

    dependabot[bot] on npm_and_yarn

    Bump js-yaml from 3.10.0 to 3.1… (compare)

  • Oct 31 20:41
    dependabot[bot] labeled #106
  • Oct 31 20:41
    dependabot[bot] opened #106
  • Oct 31 20:41
    dependabot[bot] labeled #104
  • Oct 31 20:41
    dependabot[bot] labeled #105
  • Oct 31 20:41

    dependabot[bot] on npm_and_yarn

    Bump constantinople from 3.1.0 … (compare)

  • Oct 31 20:41
    dependabot[bot] opened #105
  • Oct 31 20:41
    dependabot[bot] opened #104
  • Oct 31 20:41

    dependabot[bot] on npm_and_yarn

    Bump fstream from 1.0.11 to 1.0… (compare)

  • Oct 31 20:40

    dependabot[bot] on npm_and_yarn

    Bump sshpk from 1.13.1 to 1.16.… (compare)

  • Oct 31 20:40
    dependabot[bot] labeled #103
  • Oct 31 20:40
    dependabot[bot] opened #103
  • Oct 31 20:40
    dependabot[bot] labeled #102
  • Oct 31 20:40

    dependabot[bot] on npm_and_yarn

    Bump extend from 3.0.1 to 3.0.2… (compare)

  • Oct 31 20:40
    dependabot[bot] opened #102
  • Oct 31 20:40

    dependabot[bot] on npm_and_yarn

    Bump tar from 2.2.1 to 2.2.2 in… (compare)

  • Oct 31 20:39

    brianmhunt on npm_and_yarn

    (compare)

jclementEIT
@jclementEIT
That's in alpha3. If I use alpha5h, it complains about a Symbol definition.
Andrew Vickers
@avickers
OK, that makes sense. That’s a different kind of ES2015 shorthand, but it should work in Edge.
As for Symbols, I don’t believe there is a polyfill for them, so they are incompatible with IE
jclementEIT
@jclementEIT
Nuts. I've been using tko as a substitute for Knockout 3.4 because we're having issues with the content security policy not jivving with 3.4. Aside from somehow relaxing the CSP on the page I'm using Knockout, I'm not sure how to resolve the IE incompatibility.
Andrew Vickers
@avickers
Maybe something like https://github.com/medikoo/es6-symbol
Symbols are a new primitive, so there’s no way to get 100% coverage; however, that polyfill might be enough to allow TKO to function in IE.
Andrew Vickers
@avickers
Just be aware that if either TKO or your code rely on typeof, for instance, it will be a problem because it will be string instead of symbol.
jclementEIT
@jclementEIT
Thanks for the suggestion, @avickers. I'll try that.
Ken Hadden
@cosmoKenney
does tko incorporate knockout mapping and knockout validation?
Andrew Vickers
@avickers
@cosmoKenney Hi Kenney - I don’t believe so. I was able to pretty easily create a mapping tool by using a Proxy object with a get trap that returned properties wrapped in an Observable, and then used object destructuring to pull out what I needed.
If that doesn’t make sense, I can go look for the code :)
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 :)