Task-scheduler
A Clojure library designed for asynchronous scheduling of tasks
Require the code namespace:
(require '[jansuran03.task-scheduler.core :as scheduler])
=> nilYou can schedule a task to be executed after a certain millisecond timeout, while not blocking the current thread during
the preparation time.
wait-for-tasks allows you to wait for all scheduled tasks to finish running.
As you can see, each task also has their own ID (which will be important later):
(let [scheduler (scheduler/create-scheduler)]
(scheduler/schedule scheduler :task-1 #(println "Hello after 1 second") 1000)
(scheduler/wait-for-tasks scheduler))
Hello after 1 second
=> trueYou can also schedule events to happen in a loop after a certain interval. Calling stop will immediately terminate the scheduler
and prevent rescheduling of tasks, while letting the tasks, that already started executing, finish:
(let [scheduler (scheduler/create-scheduler)]
(scheduler/schedule-interval scheduler :task-1 #(println "Hello after 1 second") 1000)
(Thread/sleep 2500)
(scheduler/stop scheduler))
Hello after 1 second
Hello after 1 second
=> truewait-for-tasks will prevent scheduled tasks from being put back into the queue again as well, but it will also wait for all tasks
in the current queue to be executed naturally and to finish running (including those that were rescheduled and therefore re-queued).
(let [scheduler (scheduler/create-scheduler)]
(scheduler/schedule-interval scheduler :task-1 #(println "Hello after 1 second") 1000)
(scheduler/wait-for-tasks scheduler))
Hello after 1 second
=> trueCalling scheduler/stop-and-wait is a combination of stop and wait-for-tasks, canceling tasks in the queue and
waiting to finish for the ones that already started executing:
=> true
(let [scheduler (scheduler/create-scheduler)]
(scheduler/schedule scheduler :task-1 #(println "Hello after 1 second") 1000)
(scheduler/schedule scheduler :task-1 #(do (Thread/sleep 2000)
(println "Hello after 2.5 seconds")) 500)
(Thread/sleep 700)
(scheduler/wait-for-tasks scheduler))
Hello after 2.5 seconds
=> trueJust to make it clear, stop, wait-for-tasks and stop-and-wait will all terminate the main scheduler loop which
is also responsible for picking up messages from the message channel, and so you cannot schedule any tasks after
calling these functions or call stop after wait-for-tasks etc.
You can have guarantees about not scheduling multiple tasks with the same ID - these functions will also check,
whether a task with that ID is scheduled, and discard it eventually. The functions without "new" schedule
the task no matter what, even allowing to replace an interval task with a one-time task and the other way.
(let [scheduler (scheduler/create-scheduler)]
(scheduler/schedule-new scheduler :task-1 #(println "Hello 1") 1000)
(scheduler/schedule-new scheduler :task-1 #(println "Hello 2") 500)
(scheduler/schedule-new-interval scheduler :task-2 #(println "Hello 3") 300)
(scheduler/schedule-new-interval scheduler :task-2 #(println "Hello 4") 300)
(Thread/sleep 400)
(scheduler/wait-for-tasks scheduler))
Hello 3
Hello 3
Hello 1
=> trueThe most important use of the IDs is canceling a task by its ID.
(let [scheduler (scheduler/create-scheduler)]
(scheduler/schedule-new-interval scheduler :task-2 #(println "Hello 3") 300)
(scheduler/schedule-new-interval scheduler :task-2 #(println "Hello 4") 300)
(Thread/sleep 400)
(scheduler/cancel-schedule scheduler :task-2)
(scheduler/wait-for-tasks scheduler))
Hello 3
=> trueAll operations, which might or might not have succeeded, return a promise with the result:
(let [scheduler (scheduler/create-scheduler)]
[@(scheduler/cancel-schedule scheduler :foo) ; false
@(scheduler/schedule-new scheduler :foo #(println "Foo") 100) ; true
@(scheduler/schedule-new scheduler :foo #(println "Foo") 100) ; false
@(scheduler/schedule-new-interval scheduler :bar #(println "Bar") 100) ; true
@(scheduler/schedule-new-interval scheduler :bar #(println "Bar") 100) ; false
@(scheduler/cancel-schedule scheduler :foo) ; true
@(scheduler/cancel-schedule scheduler :bar)]) ; true
=> [false true false true false true true]Additionally, you can define your own task handler which should execute the task asynchronously and return immediately (blocking for a long time would block the scheduler main loop).
; default:
(scheduler/create-scheduler {:exec-fn #(clojure.core.async/go (%))})
; other examples:
(scheduler/create-scheduler {:exec-fn #(future (%))})
(let [executor (SomeExecutor/create)]
(scheduler/create-scheduler {:exec-fn #(.execute executor %)}))Copyright © 2024 Jan Šuráň
This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.