Git Product home page Git Product logo

Comments (10)

dustingetz avatar dustingetz commented on June 24, 2024

Can we see the business situation you have, that has the unwanted side effects?

from missionary.

den1k avatar den1k commented on June 24, 2024

I can explain it simply: it's part of a spreadsheet app that evaluates code in cells. When a cell is still evaluating but the user has updated the underlying code, the current execution should be cancelled in favor of the next one.

Presently evaluation is cancelled but because the task calls success with the error, the error value becomes the cell value until the next evaluation is complete.

from missionary.

xificurC avatar xificurC commented on June 24, 2024

If you need the catch-all then maybe catch missionary.Cancelled before and rethrow it?

from missionary.

den1k avatar den1k commented on June 24, 2024

Thanks. I have it working now with a version of

(let [task            (m/via m/blk
                             (try
                               (Thread/sleep 1000)
                               (catch Throwable t
                                 :cancelled-and-should-not-succeed-or-fail)))
      ignore-success? (volatile! false)
      cancel          (task #(when-not @ignore-success?
                               (println :success %))
                            #(println :fail %))]
  (println :cancelling)
  (vreset! ignore-success? true)
  (cancel))

from missionary.

leonoel avatar leonoel commented on June 24, 2024

Not sure if this is a bug.

It is expected. The task protocol mandates calling either callback exactly once because the cancellation may not be immediate and the caller must be able to know when the process was completely shut down.

Is there a way to achieve this?

Sorry for this answer, but you should not do that. Calling tasks and flows directly is low-level and dangerous, it's OK at the app entrypoint but otherwise you should always prefer functional composition.

When a cell is still evaluating but the user has updated the underlying code, the current execution should be cancelled in favor of the next one.

Depending on the cell state you expect between the user update and the resolution of the next task, ?< with ap or cp looks like a good fit, you would need to turn the task into a flow first (cf electric's offload)

from missionary.

den1k avatar den1k commented on June 24, 2024

Interesting. This is starting to make sense. However, in my case evals are triggered on the backend as part of a transaction listener which means cells are always received as collections, e.g. similar to

(let [cells [{:cell/form-str ":foo"}
             {:cell/form-str ":bar"}]]
  (m/?
    (->>
      (m/ap
        ;; fork process for every cell
        ;; to exec in **parallel**
        (let [cell        (m/?> ##Inf (m/seed cells))
              evaled-cell (m/? (m/timeout
                                 (m/via m/blk
                                        (let [{:cell/keys [cell/form-str]} cell]
                                          (assoc cell :cell/ret (eval (read-string form-str)))))
                                 1000
                                 ::timeout))]
          (if (= evaled-cell ::timeout)
            (-> cell
                (dissoc :cell/ret-pending?)
                (assoc :cell/exception? true
                       :cell/ret evaled-cell
                       :cell/ret-str (pr-str evaled-cell)))
            evaled-cell)))
      (m/reduce
        (fn [out v]
          (println v))
        []))))

; prints
{:cell/form-str :bar, :cell/ret :bar}
{:cell/form-str :foo, :cell/ret :foo}

But code would need to be aware of each cell's identity (by :db/ids) so that only evaluations of the same cell cancels its previous evaluation if one exists. Any way to achieve this?

from missionary.

dustingetz avatar dustingetz commented on June 24, 2024

Electric for-by is m/group-by under the hood. Have you considered modeling the spreadsheet with Electric for loops? Each cell is a signal. e/offload lets you call side effects (managing the task, the bridge to flow, the cancellation when something upstream changes).

from missionary.

den1k avatar den1k commented on June 24, 2024

from missionary.

den1k avatar den1k commented on June 24, 2024

So what would be an approach using functional composition that would allow cell evaluations while also cancelling evals of the same cells as new cell values transfer?

from missionary.

den1k avatar den1k commented on June 24, 2024

Got it!

(time
  (let [delays [{:id    1
                 :delay 1000}
                {:id    2
                 :delay 2000}
                {:id    1
                 :delay 500}
                {:id    1
                 :delay 200}]]
    (m/?
      (->>
        (m/ap
          (let [[k vs] (m/?> ##Inf (m/group-by :id (m/seed delays)))
                m (m/?< vs)]
            (try
              (m/? (m/sleep (:delay m) (assoc m :slept true)))
              (catch missionary.Cancelled c
                (println :cancelled m)
                (m/amb)))))
        (m/reduce
          (fn [out v]
            (println v)
            (conj out v))
          [])))))
:cancelled {:id 1, :delay 1000}
:cancelled {:id 1, :delay 500}
{:id 1, :delay 200, :slept true}
{:id 2, :delay 2000, :slept true}
"Elapsed time: 2004.552279 msecs"
=> [{:id 1, :delay 200, :slept true} {:id 2, :delay 2000, :slept true}]

from missionary.

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.