Giter VIP home page Giter VIP logo

funcool / promesa Goto Github PK

View Code? Open in Web Editor NEW
490.0 16.0 57.0 1.45 MB

A promise library & concurrency toolkit for Clojure and ClojureScript.

Home Page: https://funcool.github.io/promesa/latest/

License: Mozilla Public License 2.0

Clojure 93.81% Shell 0.16% JavaScript 6.03%
promises clojurescript clojure asynchronous-programming async-await completablefuture concurrency deferred non-blocking threads

promesa's Introduction

promesa

Clojars Project

A promise library & concurrency toolkit for Clojure and ClojureScript.

This library exposes a bunch of usefull syntactic abstractions that will considerably simplify to work with promises (in a very similar way as you will do it in JS with async/await) and many helpers from executors to concurrency patterns (bulkhead & CSP). With 0 runtime external dependencies.

Here you can look a detailed documentation.

Getting Started

deps.edn:

funcool/promesa {:mvn/version "11.0.678"}

Leiningen:

[funcool/promesa "11.0.678"]

On the REPL

(require '[promesa.core :as p])

(->> (p/promise 1)
     (p/map inc)
     (deref)
;; => 2

NOTE: example only work on JVM because the evident lack of blocking primitives on JS runtime.

Contributing

If you miss something, feel free to open an issue for a discussion. If there is a clear use case for the proposed enhacement, the PR will be more than welcome.

Testing

Run the Clojure (.clj) tests:

clojure -X:dev:test

Run the ClojureScript (.cljs) tests:

npm install
npm test

promesa's People

Contributors

alexandergunnarson avatar andreasthoelke avatar borkdude avatar fhur avatar frenchy64 avatar gnl avatar jaidetree avatar janosmeszaros avatar jetmind avatar jhacksworth avatar johnchristopherjones avatar k13gomez avatar kantuni avatar laurio avatar mainej avatar manishj15 avatar mccraigmccraig avatar miikka avatar min-ki avatar moea avatar niwinz avatar ricardojmendez avatar rmschindler avatar rwstauner avatar simshanith avatar trieloff avatar wilkerlucio avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

promesa's Issues

Timeouts on Clojure?

I noticed the timeout function is behind a cljs reader conditional and there is no Java Clojure implementation. I realise you can use deref with a timeout but it's a bit jarring when considering the existing promise chaining design. Is there an alternative option I'm not seeing?

async macro doesn't work in Lumo

I think because Lumo is self-hosted clojurescript and not hosted on JVM, the async macro doesn't work.

deps.edn:

{:deps {funcool/promesa {:mvn/version "1.9.0"}
        org.clojure/core.async {:mvn/version "0.3.465"}}}

index.cljs:

(require '[promesa.async-cljs :refer-macros [async]])

From Terminal:

lumo -c `clj -Spath` index.cljs

Backtrace:

No such namespace: java.util.concurrent.locks.Lock, could not locate java/util/concurrent/locks/Lock.cljs, java/util/concurrent/locks/Lock.cljc, or JavaScript source providing "java.util.concurrent.locks.Lock" in file cljs/core/async/impl/ioc_macros.clj
	 (new)
	 Function.cljs.core.ex_info.cljs$core$IFn$_invoke$arity$3 (NO_SOURCE_FILE <embedded>:1923:72)
	 Function.cljs.analyzer.error.cljs$core$IFn$_invoke$arity$3 (NO_SOURCE_FILE <embedded>:2555:92)
	 Function.cljs.analyzer.error.cljs$core$IFn$_invoke$arity$2 (NO_SOURCE_FILE <embedded>:2554:92)
	 (NO_SOURCE_FILE <embedded>:5553:162)
	 Object.lumo.repl.load_other (NO_SOURCE_FILE <embedded>:6351:378)
	 lumo.repl.load (NO_SOURCE_FILE <embedded>:6355:96)
	 Function.cljs.js.require.cljs$core$IFn$_invoke$arity$5 (NO_SOURCE_FILE <embedded>:5555:77)
	 Function.cljs.js.load_deps.cljs$core$IFn$_invoke$arity$7 (NO_SOURCE_FILE <embedded>:5565:231)
	 (NO_SOURCE_FILE <embedded>:5566:141)
	 Function.cljs.js.require.cljs$core$IFn$_invoke$arity$5 (NO_SOURCE_FILE <embedded>:5544:1)
	 Function.cljs.js.load_deps.cljs$core$IFn$_invoke$arity$7 (NO_SOURCE_FILE <embedded>:5565:231)
	 (NO_SOURCE_FILE <embedded>:5567:299)
	 Function.cljs.js.require.cljs$core$IFn$_invoke$arity$5 (NO_SOURCE_FILE <embedded>:5544:1)
	 Function.cljs.js.require.cljs$core$IFn$_invoke$arity$4 (NO_SOURCE_FILE <embedded>:5538:416)
	 (NO_SOURCE_FILE <embedded>:5566:423)

I am using:

Lumo 1.7.0
ClojureScript 1.9.908
Node.js v8.4.0

Which is the latest Homebrew version.

Applying non-functions

Is it reasonable to expect this to work the same in cljs as it does on the JVM?

(-> {:x 1} p/promise (p/then :x))

Similarly for multimethods, and I guess everything which does evaluate to Function. One naive fix would be to have -map, -bind and -catch wrap all inputs in fn - do you think that's a bad idea? More than happy to make the change, if not.

Protocol extension problem on js/Promise

Hello folks!

I am working on the sieppari port to ClojureScript and running into some trouble: there is a protocol on js/Promise that seem to disappear when I require promesa.core.

Now, I am not an expert here but if I recall correctly bluebird does some magic there and I was wondering if there is a way for the two promise types to coexist or maybe I just need to extend the protocol on the right object.

The native Promise extend is here while the promesa one is here.

Thank you very much in advance!

Consider publishing no-bluebird build

I think it would be great to have the choice to use this great library (and other ones depending on it) without having to pull in bluebird.js and instead use the native implementation. We would just need a different build target excluding /assets and the require in core.cljc; also removal of the (js/Promise.noConflict) call and having it published under, eg. funcool/promesa-nobb

promisify vs. bluebird's promisify

The docstring for promisify mentions it converts functions taking Node-style callbacks, though the Node convention is (error, result). Bluebird's Promise.promisify is aware of this, and will reject the promise if the injected function is passed an error, otherwise resolve w/ the second arg.

If the intention is to only support callbacks which use a single parameter, maybe it'd be clearer if the comment were adjusted to not mention Node. Or else adjust/add a different utility for cross-platform two argument promisification. More than happy to submit a PR if you think a change is justified.

(I personally only have a need for this functionality in Clojurescript, so I'm using (.promisify js/Promise ...) directly - It'd be a tiny bit easier for me to have that be (p/promisify ...), but I'm not sure what's best for the library)

p/then is called even if the promise returned an error

In this example I expected that only the error would be logged. However, I also see the log generated in the p/then. Is this expected behavior?

(-> (p/promise (ex-info "Some error" nil))
      (p/catch (fn [error] (.log js/console error)))
      (p/then  (.log js/console "Why does this still happen?")))

Issues with "special promises"

Hey! I'm not sure how to best describe this but Firebase's JS SDK uses special promises. I think they use Closure under the hood so it might be goog.promise but it's advanced compiled so it's not really apparent or usable.

While upgrading from 4.0.2 to 5.0.0 these promises no longer work with promesa out of the box.

    (-> (firebase/auth)
        (.-currentUser)
        (.getIdToken)
        (p/then (fn [token] (println token))))

As per the docs getIdToken should return a promise. Replacing p/then with .then does indeed work.

The error in the console looks like this:

Support lumo

When trying to require promesa.core, I get the following error:

Could not require org.bluebird in file promesa/impl.cljc
	 (new)
	 Function.cljs.core.ex_info.cljs$core$IFn$_invoke$arity$3 (NO_SOURCE_FILE <embedded>:1937:200)
	 Function.cljs.analyzer.error.cljs$core$IFn$_invoke$arity$3 (NO_SOURCE_FILE <embedded>:2484:92)
	 (NO_SOURCE_FILE <embedded>:5237:40)
	 Object.cljs.js.run_async_BANG_ (NO_SOURCE_FILE <embedded>:5217:173)
	 Object.cljs.js.process_deps (NO_SOURCE_FILE <embedded>:5217:244)
	 Object.cljs.js.process_libs_deps (NO_SOURCE_FILE <embedded>:5219:60)
	 (NO_SOURCE_FILE <embedded>:5235:358)
	 Object.cljs.js.run_async_BANG_ (NO_SOURCE_FILE <embedded>:5217:173)
	 Object.cljs.js.process_deps (NO_SOURCE_FILE <embedded>:5217:244)

Unexpected identifier
	 createScript (vm.cljs:53:10)
	 Object.runInThisContext (vm.cljs:95:10)
	 (Object.lumoEval)
	 lumo.repl.caching_node_eval (NO_SOURCE_FILE <embedded>:6133:194)
	 (NO_SOURCE_FILE <embedded>:5236:153)
	 Object.cljs.js.run_async_BANG_ (NO_SOURCE_FILE <embedded>:5217:173)
	 Object.cljs.js.process_deps (NO_SOURCE_FILE <embedded>:5217:244)
	 Object.cljs.js.process_libs_deps (NO_SOURCE_FILE <embedded>:5219:60)
	 (NO_SOURCE_FILE <embedded>:5235:358)
	 Object.cljs.js.run_async_BANG_ (NO_SOURCE_FILE <embedded>:5217:173)

`deliver` and `fail`?

Maybe I am missing something obvious, but wouldn't it make sense to have core-like deliver and fail functions which could be used on promise objects?

So basically:

(let [p (promise)]
   ...
   (if something (deliver p :good) (fail p (Exception. ...)))
   ...
  p)

Thanks!

Function returning native promise has to be wrapper with p/promise

; https://www.npmjs.com/package/node-fetch
(def node-fetch (node/require "node-fetch"))

(p/then (node-fetch "...") #(println "succ"))
; throws Error: No protocol method IPromise.-map defined for type object: [object Promise]

Therefore it has to be wrapped with p/promise, which is not nice, especially when we know that the function returns promise directly:

(p/then (p/promise (node-fetch "...")) #(println "succ"))

clojure.lang.ArityException: Wrong number of args (2) passed to: promesa.core/all

A sample with plet from docs doesn't work in [funcool/promesa "4.0.2"]

the following code:

(ns clojure-sample.core
  (:require [promesa.core :as p]))


@(p/plet [a (p/delay 100 1)
         b (p/delay 200 2)
         c (p/delay 120 3)]
        (+ a b c))

fails:

Exception in thread "main" Syntax error compiling at (/Users/nicolay.mitropolsky/IdeaProjects/clojure-sample/src/clojure_sample/core.clj:5:1).
	at clojure.lang.Compiler.load(Compiler.java:7647)
	at clojure.lang.Compiler.loadFile(Compiler.java:7573)
	at clojure.main$load_script.invokeStatic(main.clj:452)
	at clojure.main$script_opt.invokeStatic(main.clj:512)
	at clojure.main$script_opt.invoke(main.clj:507)
	at clojure.main$main.invokeStatic(main.clj:598)
	at clojure.main$main.doInvoke(main.clj:561)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:705)
	at clojure.main.main(main.java:37)
Caused by: java.util.concurrent.ExecutionException: clojure.lang.ArityException: Wrong number of args (2) passed to: promesa.core/all
	at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395)
	at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1999)
	at clojure.core$deref_future.invokeStatic(core.clj:2300)
	at clojure.core$deref.invokeStatic(core.clj:2320)
	at clojure.core$deref.invoke(core.clj:2306)
	at clojure_sample.core$eval892.invokeStatic(core.clj:0)
	at clojure_sample.core$eval892.invoke(core.clj)
	at clojure.lang.Compiler.eval(Compiler.java:7176)
	at clojure.lang.Compiler.load(Compiler.java:7635)
	... 9 more
Caused by: clojure.lang.ArityException: Wrong number of args (2) passed to: promesa.core/all
	at clojure.lang.AFn.throwArity(AFn.java:429)
	at clojure.lang.AFn.invoke(AFn.java:36)
	at clojure_sample.core$eval892$fn__893.invoke(core.clj:5)
	at clojure.core$comp$fn__5792.invoke(core.clj:2569)
	at promesa.util.FunctionWrapper.apply(util.cljc:43)
	at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1106)
	at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2235)
	at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:143)
	at promesa.impl$eval649$fn__652.invoke(impl.cljc:120)
	at promesa.protocols$eval177$fn__199$G__162__212.invoke(protocols.cljc:28)
	at promesa.impl$eval675$fn__678.invoke(impl.cljc:177)
	at promesa.protocols$eval177$fn__199$G__162__212.invoke(protocols.cljc:28)
	at promesa.core$then.invokeStatic(core.cljc:152)
	at promesa.core$then.invoke(core.cljc:142)
	at promesa.core$bind.invokeStatic(core.cljc:169)
	at promesa.core$bind.invoke(core.cljc:167)
	at clojure_sample.core$eval892.invokeStatic(core.clj:5)
	... 12 more

Missing properties in externs blocks advanced optimizations

When building my app for production, I get the following message when run:

Uncaught TypeError: Promise.Ci is not a function(anonymous function) @ app.js:891

The referred section of the code is:

var tF=Promise.Ci();tF.log(Ny,{cancellation:!0,warnings:!1},void 0);h=tF.prototype;h.Gg=f

I'm using httpurr 0.6.1 with promesa 1.4.0. The API from promesa referred to in my app is:

map
error
all
delay
resolved
alet
await

Any ideas?

alet with interop forms produces invalid JavaScript

With

[org.clojure/clojurescript "1.9.562"]
[funcool/promesa "1.8.1"]

The CLJS

(ns promesabug.core
  (:require [promesa.core :as p :include-macros true]))

(p/alet [a-binding (.aJSMethod (js-obj))]
  nil)

produces

// Compiled by ClojureScript 1.9.562 {:target :nodejs}
goog.provide('promesabug.core');
goog.require('cljs.core');
goog.require('promesa.core');
if(cljs.core._EQ_.call(null,promesa.core.await$,.aJSMethod)){
promesa.core.bind.call(null,{},(function (a_binding){
return promesa.core.promise.call(null,null);
}));
} else {
var a_binding_8847 = {}.aJSMethod();
promesa.core.promise.call(null,null);
}

//# sourceMappingURL=core.js.map

which includes cljs.core._EQ_.call(null,promesa.core.await$,.aJSMethod). .aJSMethod is nonsense JS here.

This appears to be what happens when you take the value of an interop symbol in ClojureScript; for instance, (println .aJSMethod) becomes cljs.core.println.call(null,.aJSMethod);. I'd call that a bug in ClojureScript, but until and unless that's changed, it would be nice to avoid causing the problem.

(A decent workaround for the current version of Promesa, incidentally, is to wrap the right-hand-side of the binding in (p/await …).)

Support for futures, executors?

Hi Andrey. My team has an internal library for using completablefuture in Clojure, but we'd rather use an open source lib. Promesa provides A lot of what we need, but there are a couple of gaps:

  • a macro that works like (future ...) but returns a CompletableFuture
  • ability to specify the executor for callbacks, including using thenApply vs thenApplyAsync

Would you be willing to accept contributions to support these things?

Allow renaming of await in alet macro

This code currently fails with the error "Error: Should be only used in alet macro."

(ns catfacts.scratch
  (:require [promesa.core :as p
             :refer [await]
             :rename {await <!}
             :refer-macros [alet]]))

(def nodegit (js/require "nodegit"))
(def repo (.-Repository nodegit))

(-> (alet [r (<! (.open repo ".."))
             commit (<! (.getBranchCommit r "master"))
             message (<! (.message commit))]
            (println message))
    (p/catch  (fn [err]
                (println "ERROR:" err))))

clojure.test/is doesn't work inside then

I've found out quiet strange behaviour of promesa and is macro when using Clojure version, consider those tests:

#?(:cljs
   (deftest cljc-p-test
     (async done
            (p/then (p/promise 42) (fn [_]
                                     (is (= 2 1))
                                     (done))))))
#?(:clj
   (deftest cljc-p-test
     @(p/then (p/promise 42) (fn [_]
                               (is (= 2 1))))))

ClojureScript version fails like it should, but Clojure test always passes. If I evaluate following line @(p/then (p/promise 42) (fn [_] (is (= 2 1)))) it will return false without throwing anything, it will be resolved.

I'm not sure what may cause such thing, I'll appreciate any help.

p/all returns js array

Is there a reson why (p/all ...) returns js array instead of cljs sequence? Is this a bug in 1.8.0 or desired behavior?

Some examples

Not working

(defn question-sets
  [creds env org-id]
  (->> (p/all [(enabled-question-set-items creds env "*")
               (enabled-question-set-items creds env org-id)])
       ;(p/map js->clj)
       (p/map flatten)
       (p/map #(-> {:question-sets %}))))

Working

(defn question-sets
  [creds env org-id]
  (->> (p/all [(enabled-question-set-items creds env "*")
               (enabled-question-set-items creds env org-id)])
       (p/map js->clj)
       (p/map flatten)
       (p/map #(-> {:question-sets %}))))

Working

(defn question-sets
  [creds env org-id]
  (->> (p/all [(enabled-question-set-items creds env "*")
               (enabled-question-set-items creds env org-id)])
       (p/map (fn [[qs1 qs2]]
                (into [] (concat qs1 qs2))))
       (p/map #(-> {:question-sets %}))))

Type outputs

(defn question-sets
  [creds env org-id]
  (->> (p/all [(enabled-question-set-items creds env "*")
               (enabled-question-set-items creds env org-id)])
       (p/map #(do (println "array?" (array? %))
                   (-> %)))
       (p/map (fn [[qs1 qs2]]
                (do
                  (println "qs1.array?" (array? qs1))
                  (println "qs2.array?" (array? qs2))
                  (into [] (concat qs1 qs2)))))
       (p/map #(do (println "array?" (array? %))
                   (-> %)))
       (p/map #(-> {:question-sets %}))))

->

array? true
qs1.array? false
qs2.array? false
array? false

Reflection Warning

with

[funcool/promesa "4.0.2"]

=>

Reflection warning, promesa/exec.cljc:276:18 - call to method schedule on java.util.concurrent.ScheduledExecutorService can't be resolved (argument types: unknown, unknown, java.util.concurrent.TimeUnit).

kitchen-async style p/do

What I'm looking for is a simple way to run multiple promise-returning forms sequentially, e.g.:

(require '[kitchen-async.promise :as ka])
(ka/do 
 (if (> (rand) 0.5)
   (do (println "delaying 1s")
       (p/delay 1000))
   (do (println "delaying 3s")
       (p/delay 3000)))
 (println "done1")
 (p/delay 1000)
 (println "done2"))

This would print the different lines with some delays. Does promesa have an API for this kind of thing or is it not really in the scope of the project?

There is do* in promesa but I don't fully understand the docstrings of that and attempt. Some quick testing with the example above suggested that it's meant for something different.

edit: Oh and thanks for all the amazing recent work on promesa, I really appreciate it's lightweightness after bluebird was dropped and the well maintained changelog! 👍


Somewhat relatedly I have this in a project I'm currently working on — would be curious if there's more elegant ways to handle this using promisa's APIs.

(defn doall-serial
  "Apply `f` to every element in `xs`, chaining all the returned promises so
  that every element is processed one by one and not in parallel.

  It is expected that `f` returns a promise. Kind of like doseq."
  [f xs]
  (loop [[x & xs] xs
         p (p/resolve nil)]
    (when x
      (recur
       xs
       (-> p
           (p/then (fn [] (f x)))
           (p/catch* (fn [err]
                       (js/console.error err)
                       (throw err))))))))

Not all JS tests are executed

Steps to reproduce

./scripts/build
node out/tests.js

What should happen

All tests should be executed.

What actually happens

Testing promesa.issue-36

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Additional info

node --version returns “v6.9.4”.
lein --version returns “Leiningen 2.7.1 on Java 1.8.0_121 OpenJDK 64-Bit Server VM”.

Bizarre loading failure

(require '[promesa.core :as pc])
Error: ENOENT: no such file or directory, open 'G:\dev\main\.cljs_node_repl�luebird�luebird.js'
    at Error (native)
    at Object.fs.openSync (fs.js:549:18)
    at Object.fs.readFileSync (fs.js:397:15)
    at Object.nodeGlobalRequire (repl:85:28)
    at Object.cljs$core$load_file [as load_file] (G:\dev\main\out\.cljs_node_repl\cljs\core.js:341:13)

This is using promesa 1.9.0, Clojurescript 1.9.908. I cannot reproduce under linux, so I assume this is a windows path problem? (\\b is an ascii control code escape sequence…)

Cannot compile in cljs - reserved words error

I'm getting the following warnings when I add promesa to the require list for a cljs namespace:

WARNING - Keywords and reserved words are not allowed as unquoted property names in older versions of JavaScript. If you are targeting newer versions of JavaScript, set the appropriate language_in option.
return it__$1.catch(((function (it__$1){
              ^

WARNING - Keywords and reserved words are not allowed as unquoted property names in older versions of JavaScript. If you are targeting newer versions of JavaScript, set the appropriate language_in option.
return p.finally(callback);

I've tried updating my :language-in and :language-out settings to be similar to what you're using in the script directory, but the warnings (which halt compilation on :output :advanced) persist. Any tips?

undefined is not an object (evaluating 'promesa.core.alet.call')

This is my code:

(require '[promesa.core :as p])

(def async-storage
  (goog.object/get (js/require "react-native") "AsyncStorage"))

(p/alet [item (p/await (.getItem async-storage app-key))] 
  item)

I'm just trying to mimic this JS syntax:

const value = await AsyncStorage.getItem(app-key);

I get this error:

screen shot 2017-08-05 at 3 30 03 pm

Java 12 CompletionStage.exceptionallyAsync method is used

I am getting a reflection warning when compiling my project using promesa:

Reflection warning, promesa/impl.cljc:178:11 - call to method exceptionallyAsync on java.util.concurrent.CompletionStage can't be resolved (no such method).

The method was introduces in Java 12.

The docs say:

On the JVM paltform promesa is built on top of completable futures (requires jdk>=8)

nodejs always waits for timeout

I'm using promesa within a NodeJS script to interact with a remote server via an asynchronous API. The main function of the script essentially looks like this:

(let [promise  (p/deferred)
          callback (fn [v] (p/resolve! promise v))]
      (async-call-api callback)
      (-> promise
          (timeout* 5000)
          (p/then (fn [result] (println "result: " result "\n")))
          (p/catch #(println "command timed out"))))

Because timeout indirectly creates a timer, NodeJS will wait for the result (assuming the server responds) because it will not terminate while there are still active timers. Unfortunately, the timer is never cancelled, so NodeJS will always wait for the timeout (5s here) to expire, even if the result is available sooner.

I can work around this by changing the timeout implementation to:

(defn timeout*
  "Returns a cancellable promise that will be fulfilled with this
  promise's fulfillment value or rejection reason.  However, if this
  promise is not fulfilled or rejected within `ms` milliseconds, the
  returned promise is cancelled with a TimeoutError"
  ([p t] (timeout* p t ::default exec/default-scheduler))
  ([p t v] (timeout* p t v exec/default-scheduler))
  ([p t v scheduler]
   (c/let [timeout (p/deferred)]
     (let [tid             (exec/schedule! scheduler t #(if (= v ::default)
                                                          (p/reject! timeout (js/Error. "Operation timed out."))
                                                          (p/resolve! timeout v)))
           wrapped-promise (p/then p (fn [result] (pt/-cancel! tid) result))]
       (p/race [wrapped-promise timeout])))))

This explicitly cancels the timeout task if the given promise returns. Is there a downside to cancelling the timeout task explicitly? Should this be the default behavior for timeout? I don't know if this would have negative effects when used from a browser or from Clojure.

Optimizations advanced Uncaught TypeError: Cannot set property '_bitField' of undefined (possible dependency problem)

Previously created funcool/httpurr#8 but moved to here.

Create a namespace similar to the below and compile it using :optimizations :advanced. Run the main function and in a browser and you will see an error that says Uncaught TypeError: Cannot set property '_bitField' of undefined.

(ns bitfield-error.core
  (:require [cognitect.transit :as t]
            [httpurr.client :as http]))

(enable-console-print!)

(defn encode
  [data]
  (let [w (t/writer :json)]
    (t/write w data)))

(defn ^:export main
  []
  (println "encoded" (encode {:foo "bar"})))

Now comment out the :require containing [httpurr.client :as http], refresh the browser, and you should see encoded ["^ ","~:foo","bar"] printed to the console (no error).

I have created a minimal working example here: https://github.com/kennyjwilli/bitfield-error. See the README for more info on how to use the project.

Exceptions don't get caught by "catch" when thrown in "promise" body

I noticed that thrown exceptions apparently don't get caught by catch clauses when they are thrown from within the promise body:

(-> (p/promise (throw (ex-info "foo" {})))
    (p/catch (fn [e] (println "ERROR" e))))

results in an (unexpectedly) uncaught exception:

Execution error (ExceptionInfo) at user/eval61083 (form-init3437190804973754551.clj:1).
foo
class clojure.lang.ExceptionInfo
  form-init3437190804973754551.clj:	1	user/eval61083
  form-init3437190804973754551.clj:	1	user/eval61083
  Compiler.java:	7177	clojure.lang.Compiler/eval
  Compiler.java:	7132	clojure.lang.Compiler/eval
  core.clj:	3214	clojure.core/eval
...

Contrary to that, when the exception gets thrown from within then, it does get caught by the catch clause:

(-> (p/promise nil)
    (p/then (fn [_] (throw (ex-info "foo" {}))))
    (p/catch (fn [e] (println "ERROR" e))))

results in the (expected) behavior:

ERROR #error {
 :cause foo
 :data {}
 :via
 [{:type clojure.lang.ExceptionInfo
   :message foo
   :data {}
   :at [user$eval61087$fn__61088 invoke form-init3437190804973754551.clj 2]}]
 :trace
 [[user$eval61087$fn__61088 invoke form-init3437190804973754551.clj 2]
  [clojure.core$comp$fn__5807 invoke core.clj 2569]
  [promesa.util.FunctionWrapper apply util.cljc 43]
...
  [java.lang.Thread run Thread.java 748]]}

#object[java.util.concurrent.CompletableFuture 0x65ecd6d "resolved"]

Am I missing anything?

Does not play well with JS promises

Promesa promises don't play well with JS promises. I keep getting protocol errors and having to wrap JS promises inside promesa promises inside JS promises and so on.

Thank you for your work, but please put a huge disclaimer somewhere so that others don't end up wasting as much time as I did.

No promise library > false promise library.

Async macro exception handling

Not sure if this is a bug or just me not understanding it correctly.

When using the async macro in ClojureScript, I am unable to handle thrown exceptions. I've tried attaching a (p/catch) handler to the value returned from the async macro, as well as wrapping the p/await call inside a try/catch block, but any exceptions thrown during a p/await call goes unhandled.

The p/catch function does get called, but I still also get an Unhandled rejection error.

bind vs. then

The way I use promesa, it'd be more convenient if the behaviour of then was to bind any promise returned by a callback. The cost of having to wrap/disguise a promise which ought to be returned as-is seems worth paying, because I think that's a relatively rare use case.

If that change would be too far-reaching, an alternative to then which binds if it encounters a promise would be just as helpful - or changing bind so it can accept functions which don't return promises, as well - basically I don't want to have to care whether a callback returns a promise or a resolved value. What do you think?

Resolve and reject pormises with cljs-ajax

I am attempting to use the promesa library with cljs-ajax. I am unsure how to resolve / reject a promise in conjunction with the requests generated from that library.

Is this something that you have any experience with?

I am attempting to do something similar to the following.

(def user-info (ratom/atom {:username "" :password "" :email ""}))

;; ====
;  Registration
(defn register-error-handler [{:keys [status status-text]}]
  (.log js/console "Registration error"))

(defn register-response-handler [response]
  (.log js/console "Registration was a success"))

(defn register-new-user []
  (POST "https://example.com/api/user" {:params        {:password (:password @user-info)
                                                        :username (:username @user-info)
                                                        :email    (:email    @user-info)}
                                        :handler       register-response-handler
                                        :error-handler register-error-handler}))

;; ====
;  Login
(defn login-error-handler [{:keys [status status-text]}]
  (.log js/console "Login Error"))

(defn login-response-handler [response]
  (.log js/console "Handle login"))

(defn auth-header [username password]
  (str "Basic " (b64/encodeString (str username ":" password))))

(defn attempt-login [username password]
  (GET "https://example.com/api/auth" {:headers         {"Authorization" (auth-header username password)}
                                       :handler         login-response-handler
                                       :error-handler   login-error-handler
                                       :response-format :json
                                       :keywords?       true
                                       :prefix          true}))

;; ==========
;  What is the proper way to resove/reject the promise when registering a user?

(defn register-and-login-new-user []
  (-> (p/promise register-new-user)
      (p/then    (sf/attempt-login (:username @user-info) (:password @user-info)))))

Can't build with shadow-cljs

I'm trying to build a small script that uses promesa and shadow-cljs, but it looks like the handing of the bluebird namespace trips shadow-cljs up:

ClojureScript file:

(ns onto.server
  (:require [httpurr.client :as http]
            [httpurr.client.node :refer [client]]))
 
(defn main [args]
  (println "hello")
  (+ 1 1))

shadow-cljs.edn

{:source-paths
 ["src"]
 :dependencies
 [
   [funcool/httpurr "1.0.0"]
 ]
 :builds
 {:app {
   :target :node-script
   :output-dir "target"
   :output-to "onto-server.js"
   :main onto.server/main
 }}}

resulting error message:

$ shadow-cljs compile app
shadow-cljs - config: /code/…/shadow-cljs.edn version: 2.0.77
shadow-cljs - starting ...
[:app] Compiling ...
The required namespace "org.bluebird" is not available, it was required by "promesa/impl.cljc".

Lumo script with promesa

Hello friends,

I am building a script for Lumo/Node using puppeteer which heavily use Promises.

I was looking for a more cljs way to handle Promise, I understand that promesa can help me with creating Promise but what about easily handling them ? Do you have any advices ?

Thanks a lot
Baptiste

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.