Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
Frank Lemanschik
@frank-dspeed
but this values get async created :)
Frank Lemanschik
@frank-dspeed
Ah don't mind i got it working with a combination of flatten :)
and scan
Reko Tiira
@rekotiira

@rpominov I bumped to a situation like this with Kefir:

stream
    .flatMapConcat(it => Kefir.fromPromise(funcThatProducesPromise(it)))
    .log('value')

One would expect that there would only be one funcThatProducesPromise running at any given time, but for some reason Kefir calls the function that produces the flatmapped stream eagerly, and buffers the produced streams instead, effectively calling funcThatProducesPromise as soon as stream produces new value, even if the previous one is still running. To me this seems pretty confusing design choice, I think it'd be more logical to buffer the inputs and only call the producer function when the previous stream has ended (this obviously affects flatMapConcurLimit as well). I tested the same thing with Bacon to see whether it's just me who feels this is wrong, but seems like at least Bacon does it the way I'd expect. I "fixed" it by making the promise-function calling lazy by doing:

.flatMapConcat(it => Kefir.constant().flatMap(() => Kefir.fromPromise(funcThatProducesPromise(it))))

But still, I'm wondering if this can be considered as a "bug". It can lead to quite substantial bugs as well, for example in my case I was implementing a worker queue with concurrency limit using flatMapConcurLimit, and it worked fine when there weren't new jobs coming that often, but sometimes when multiple jobs started in short time, the worker queue ended up draining all connections from postgre pool, which caused the whole worker process to completely hang and not do anything. Nasty.

Roman Pominov
@rpominov
Hi, @rekotiira . Good question! Kefir runs function eagerly because these functions are supposed to be pure, and if laziness is required it supposed to be provided by streams themselves (a stream doesn't do side effects before one subscribes to it). But Promises do break this reasoning :(. Creating a promise is always effectfull if promise does something useful. I think what flatMapConcat does is correct, but there is a problem in Kefir.fromPromise API, it should also accept a function returning a promise, i.e. both Kefir.fromPromise(promise) and Kefir.fromPromise(() => promise) should work. I actually thought it's done that way already, but turns out it's not.
To clarify Kefir.fromPromise(() => promise) would call provided function lazily only when someone subscribes to the observable.
Sorry for super slow response, btw. Been very busy at work this week.
Vesa Karvonen
@polytypic
@rpominov What is your stance on optimizing Kefir? I've been reading the Kefir codebase and I believe there are several low hanging fruits for optimization. For example, every observable now includes a couple of extra fields (_logHandlers and _spyHandlers) for debugging. Eliminating those when they are not used would slow down things when debug features are used, but would reduce memory usage when they are not used. Another example is the way remove and add in Dispatcher work: a sequence of n add operations followed by n remove operations perform as O(n^2) (https://accidentallyquadratic.tumblr.com/). That can be improved so that typical patterns, i.e. remove in reverse order of add, run in O(n) time. A third example would be that the implementation currently seems to create lots of copies of event objects unnecessarily. Avoiding unnecessary copying could give a performance boost as avoiding allocations really seems to be one of the keys to improving JS perf. Shall I create PRs for these optimizations?
Roman Pominov
@rpominov
Hey, @polytypic ! After most.js came out I've gave up on optimizations in Kefir to be really honest. JS vms are so complicated, that you often get unexpected results from experiments with performance, and creating correct benchmarks is also very tricky business. One needs to be a real expert in vms to do this right. So don't expect any optimizations from me, haha :) . But if you want to work on this I'll gladly merge the PRs, but you will have to start from creating some benchmarks, so we would be sure those optimization work in practice :) . Or if you have a big app written using Kefir, benchmarking that app probably would be even better than synthetic micro benchmarks.
Vesa Karvonen
@polytypic
Sounds good. I'm also not interested in trying to optimize Kefir to match Most.js. (I'm actually quite familiar with the basic techniques/ideas that Most.js relies on. For example, I experimented with stream fusion a decade ago - damn I'm getting old.) The above are just some things I've actually observed when running my own (non-automated) frontend benchmarks. So, next time I see those issues in a profiler, I'll create a PR. :)
Roman Pominov
@rpominov
OK, sounds great!
Jack Jennings
@jackjennings
I build a small module for combining Kefir streams into a series of objects, but I'm stuck on what to call it: https://github.com/standard-library/kefir-spec
Currently I'm calling it "spec", but I don't like that this implies that it is a specification of Kefir—possibly "evolve" or maybe "blueprint"
Not sure what language to use here. Any thoughts?
Frank Lemanschik
@frank-dspeed
that exists already
if you merge 2 streams you get a serial stream
if you zip them you get always 2 pairs
the result of merge should be equal isn't it
i don't even understand your spec result :)
Jack Jennings
@jackjennings
The spec function takes a JS object where the values are streams, and emits objects in the same structure but with the last stream value in each key/value pair.
If this exists in core I would be happy to see how
Jack Jennings
@jackjennings
Juha Paananen
@raimohanska
This look almost exactly like Bacon.combineTemplate
There seems to be a Kefir version too: https://github.com/ahomu/kefir.combinetemplate
Jack Jennings
@jackjennings
✌️
Frank Lemanschik
@frank-dspeed
it looks a bit like what we do with canjs
and our can-computes and can-stream-kefir
if your searching for a dom diffing framework
that suports kefir streams to update values realtime
we have nice realtime template bindings for streams :D
Ahmed Fasih
@fasiha
Is there a way to reduce or fold a Kefir stream into a scalar value? I’m looking for max specifically.
Ahmed Fasih
@fasiha
Ah, I can scan and last :)
Inkata
@Inkata
Is this the right place to ask kefirjs questions?
Juha Paananen
@raimohanska
Damn right!
Bruno Pinto
@bpinto

Hi there, I've started using kefir recently in conjunction with karet (react) and there is one thing I'm trying to achieve but I'm not sure if I'm on the right track.

In order to mitigate potential untracked subscriptions, I would like to count the number of active observables after a test expect(countOfActiveObservables).toEqual(0). I have looked at the codebase and this doesn't exist so I'm trying to create this via a Proxy but I'm not sure if the approach I'm working on is a good one.

I've noticed that there is a _setActive method on K.Observable that maye be what I'm looking for, and I used the following code to track number of active subscriptions:

function proxy(f) {
  return new Proxy(f, {
    apply(target, thisArg, args) {
      if (thisArg._active && !args[0]) { count -= 1 }
      if (!thisArg._active && args[0]) { count += 1 }
      target.apply(thisArg, args)
    }
  });
}

K.Observable.prototype._setActive = proxy(K.Observable.prototype._setActive)

Would this count subscriptions correctly? Should I be looking at another function? Thanks for any advice

Bruno Pinto
@bpinto
I've came up with another idea, a test where I had forgotten to unsubscribe failed with this code but not sure if it wouldn't break objects that define _onActivation:
'use strict'

const Kefir = require('kefir')

// from kefir/utils/objects/extend
function extend (target /*, mixin1, mixin2... */) {
  for (let i = 1; i < arguments.length; i++) {
    for (const prop in arguments[i]) {
      target[prop] = arguments[i][prop]
    }
  }

  return target
}

extend(Kefir.Observable.prototype, {
  _onActivation () {
    Kefir.activeObservables.push(this)
  },
  _onDeactivation () {
    Kefir.activeObservables.splice(Kefir.activeObservables.indexOf(this), 1)
  },
  initActiveObservables () {
    Kefir.activeObservables = []
  }
})

module.exports = Kefir
Bruno Pinto
@bpinto

I solved the above problem of redefining _onActivation and onDeactivation by doing this:

const originalOnActivation = Kefir.Observable.prototype._onActivation
const originalOnDeactivation = Kefir.Observable.prototype._onDeactivation

extend(Kefir.Observable.prototype, {
  _onActivation () {
    Kefir.activeObservables.push(this)
    originalOnActivation()
  },
  _onDeactivation () {
    Kefir.activeObservables.splice(Kefir.activeObservables.indexOf(this), 1)
    originalOnDeactivation()
...

and now it calls both functions. I didn't need to use Proxy.