├── go.sum ├── go.mod ├── doc.go ├── codecov.yml ├── examples_test.go ├── Makefile ├── .github ├── settings.yml ├── release.yml └── workflows │ ├── lint.yml │ ├── go.yml │ ├── tweet-release.yml │ └── atomicgo.yml ├── LICENSE ├── Taskfile.yml ├── .gitignore ├── schedule.go ├── README.md └── .golangci.yml /go.sum: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module atomicgo.dev/schedule 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package schedule provides a simple scheduler for Go. 3 | 4 | It can run a function at a given time, in a given duration, or repeatedly at a given interval. 5 | */ 6 | package schedule 7 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # ┌───────────────────────────────────────────────────────────────────┐ 2 | # │ │ 3 | # │ IMPORTANT NOTE │ 4 | # │ │ 5 | # │ This file is synced with https://github.com/atomicgo/template │ 6 | # │ │ 7 | # │ Please apply all changes to the template repository │ 8 | # │ │ 9 | # └───────────────────────────────────────────────────────────────────┘ 10 | 11 | coverage: 12 | status: 13 | project: 14 | default: 15 | informational: true 16 | patch: 17 | default: 18 | informational: true 19 | -------------------------------------------------------------------------------- /examples_test.go: -------------------------------------------------------------------------------- 1 | package schedule_test 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "atomicgo.dev/schedule" 8 | ) 9 | 10 | func ExampleAfter() { 11 | task := schedule.After(5*time.Second, func() { 12 | fmt.Println("5 seconds are over!") 13 | }) 14 | 15 | fmt.Println("Some stuff happening...") 16 | 17 | task.Wait() 18 | } 19 | 20 | func ExampleAt() { 21 | task := schedule.At(time.Now().Add(5*time.Second), func() { 22 | fmt.Println("5 seconds are over!") 23 | }) 24 | 25 | fmt.Println("Some stuff happening...") 26 | 27 | task.Wait() 28 | } 29 | 30 | func ExampleEvery() { 31 | task := schedule.Every(time.Second, func() bool { 32 | fmt.Println("1 second is over!") 33 | 34 | return true // return false to stop the task 35 | }) 36 | 37 | fmt.Println("Some stuff happening...") 38 | 39 | time.Sleep(10 * time.Second) 40 | 41 | task.Stop() 42 | } 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # ┌───────────────────────────────────────────────────────────────────┐ 2 | # │ │ 3 | # │ IMPORTANT NOTE │ 4 | # │ │ 5 | # │ This file is synced with https://github.com/atomicgo/template │ 6 | # │ │ 7 | # │ Please apply all changes to the template repository │ 8 | # │ │ 9 | # └───────────────────────────────────────────────────────────────────┘ 10 | 11 | test: 12 | @echo "# Running tests..." 13 | @go test -v ./... 14 | 15 | lint: 16 | @echo "# Linting..." 17 | @echo "## Go mod tidy..." 18 | @go mod tidy 19 | @echo "## Fixing whitespaces..." 20 | @wsl --allow-cuddle-declarations --force-err-cuddling --force-case-trailing-whitespace 3 --fix ./... 21 | @echo "## Running golangci-lint..." 22 | @golangci-lint run 23 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | _extends: .github 2 | 3 | repository: 4 | # See https://developer.github.com/v3/repos/#edit for all available settings. 5 | 6 | # A short description of the repository that will show up on GitHub 7 | description: ⏰ Easily schedule non-blocking tasks in Go. Supports durations, specific times and intervals. 8 | 9 | # A comma-separated list of topics to set on the repository 10 | topics: atomicgo, go, golang, golang-library, scheduler, time, ticker, schedule, timer, hacktoberfest 11 | 12 | # Either `true` to make the repository private, or `false` to make it public. 13 | private: false 14 | 15 | # Either `true` to enable issues for this repository, `false` to disable them. 16 | has_issues: true 17 | 18 | # Either `true` to enable projects for this repository, or `false` to disable them. 19 | # If projects are disabled for the organization, passing `true` will cause an API error. 20 | has_projects: false 21 | 22 | # Either `true` to enable the wiki for this repository, `false` to disable it. 23 | has_wiki: false 24 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | # ┌───────────────────────────────────────────────────────────────────┐ 2 | # │ │ 3 | # │ IMPORTANT NOTE │ 4 | # │ │ 5 | # │ This file is synced with https://github.com/atomicgo/template │ 6 | # │ │ 7 | # │ Please apply all changes to the template repository │ 8 | # │ │ 9 | # └───────────────────────────────────────────────────────────────────┘ 10 | 11 | changelog: 12 | exclude: 13 | labels: 14 | - ignore-for-release 15 | authors: 16 | - octocat 17 | categories: 18 | - title: Breaking Changes 🛠 19 | labels: 20 | - breaking 21 | - title: Exciting New Features 🎉 22 | labels: 23 | - feature 24 | - title: Fixes 🔧 25 | labels: 26 | - fix 27 | - title: Other Changes 28 | labels: 29 | - "*" 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Marvin Wendt (aka. MarvinJWendt) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Taskfile.yml: -------------------------------------------------------------------------------- 1 | # ┌───────────────────────────────────────────────────────────────────┐ 2 | # │ │ 3 | # │ IMPORTANT NOTE │ 4 | # │ │ 5 | # │ This file is synced with https://github.com/atomicgo/template │ 6 | # │ │ 7 | # │ Please apply all changes to the template repository │ 8 | # │ │ 9 | # └───────────────────────────────────────────────────────────────────┘ 10 | 11 | version: "3" 12 | 13 | tasks: 14 | test: 15 | desc: Run all tests 16 | cmds: 17 | - go test ./... 18 | tdd: 19 | desc: Test Driven Development - Watch tests 20 | watch: true 21 | sources: 22 | - "**/*.go" 23 | cmds: 24 | - go test ./... 25 | 26 | lint: 27 | desc: Run all linters 28 | cmds: 29 | - go mod tidy 30 | - wsl --allow-cuddle-declarations --force-err-cuddling --force-case-trailing-whitespace 3 --fix ./... 31 | - golangci-lint run 32 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | # ┌───────────────────────────────────────────────────────────────────┐ 2 | # │ │ 3 | # │ IMPORTANT NOTE │ 4 | # │ │ 5 | # │ This file is synced with https://github.com/atomicgo/template │ 6 | # │ │ 7 | # │ Please apply all changes to the template repository │ 8 | # │ │ 9 | # └───────────────────────────────────────────────────────────────────┘ 10 | 11 | name: Code Analysis 12 | 13 | on: [push, pull_request] 14 | 15 | jobs: 16 | lint: 17 | if: "!contains(github.event.head_commit.message, 'autoupdate')" 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | - name: Set up Go 23 | uses: actions/setup-go@v4 24 | with: 25 | go-version: "stable" 26 | 27 | - name: golangci-lint 28 | uses: golangci/golangci-lint-action@v3 29 | with: 30 | version: latest 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ┌───────────────────────────────────────────────────────────────────┐ 2 | # │ │ 3 | # │ IMPORTANT NOTE │ 4 | # │ │ 5 | # │ This file is synced with https://github.com/atomicgo/template │ 6 | # │ │ 7 | # │ Please apply all changes to the template repository │ 8 | # │ │ 9 | # └───────────────────────────────────────────────────────────────────┘ 10 | 11 | # Binaries 12 | *.exe 13 | *.exe~ 14 | *.so 15 | 16 | # Go specifics 17 | 18 | ## Test binary, built with `go test -c` 19 | *.test 20 | 21 | ## Output of the go coverage tool 22 | *.out 23 | 24 | ## Vendored dependencies 25 | vendor/ 26 | 27 | # IDEs 28 | 29 | ## IntelliJ 30 | .idea 31 | *.iml 32 | out 33 | gen 34 | 35 | ## Visual Studio Code 36 | .vscode 37 | *.code-workspace 38 | 39 | # Operating System Files 40 | 41 | ## macOS 42 | .DS_Store 43 | 44 | # Other 45 | 46 | ## Experimenting folder 47 | experimenting 48 | 49 | ## CI assets 50 | .templates 51 | 52 | ## Taskfile 53 | .task 54 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # ┌───────────────────────────────────────────────────────────────────┐ 2 | # │ │ 3 | # │ IMPORTANT NOTE │ 4 | # │ │ 5 | # │ This file is synced with https://github.com/atomicgo/template │ 6 | # │ │ 7 | # │ Please apply all changes to the template repository │ 8 | # │ │ 9 | # └───────────────────────────────────────────────────────────────────┘ 10 | 11 | name: Go 12 | 13 | on: 14 | pull_request: 15 | 16 | jobs: 17 | test: 18 | name: Test Go code 19 | runs-on: ${{ matrix.os }} 20 | strategy: 21 | matrix: 22 | os: [ubuntu-latest, windows-latest, macos-latest] 23 | steps: 24 | - name: Set up Go 25 | uses: actions/setup-go@v4 26 | with: 27 | go-version: stable 28 | 29 | - name: Check out code into the Go module directory 30 | uses: actions/checkout@v3 31 | 32 | - name: Get dependencies 33 | run: go get -v -t -d ./... 34 | 35 | - name: Build 36 | run: go build -v . 37 | 38 | - name: Test 39 | run: go test -coverprofile="coverage.txt" -covermode=atomic -v -p 1 . 40 | 41 | - name: Upload coverage to Codecov 42 | uses: codecov/codecov-action@v1 43 | -------------------------------------------------------------------------------- /.github/workflows/tweet-release.yml: -------------------------------------------------------------------------------- 1 | # ┌───────────────────────────────────────────────────────────────────┐ 2 | # │ │ 3 | # │ IMPORTANT NOTE │ 4 | # │ │ 5 | # │ This file is synced with https://github.com/atomicgo/template │ 6 | # │ │ 7 | # │ Please apply all changes to the template repository │ 8 | # │ │ 9 | # └───────────────────────────────────────────────────────────────────┘ 10 | 11 | name: Tweet release 12 | 13 | # Listen to the `release` event 14 | on: 15 | release: 16 | types: [published] 17 | 18 | jobs: 19 | tweet: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: Eomm/why-don-t-you-tweet@v1 23 | # We don't want to tweet if the repository is not a public one 24 | if: ${{ !github.event.repository.private }} 25 | with: 26 | tweet-message: 27 | "New ${{ github.event.repository.name }} release: ${{ github.event.release.tag_name }}! 🎉 28 | 29 | Try it out: atomicgo.dev/${{ github.event.repository.name }} 30 | 31 | #go #golang #opensource #library #release #atomicgo" 32 | env: 33 | TWITTER_CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }} 34 | TWITTER_CONSUMER_API_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }} 35 | TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }} 36 | TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} 37 | -------------------------------------------------------------------------------- /schedule.go: -------------------------------------------------------------------------------- 1 | package schedule 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | // Task holds information about the running task and can be used to stop running tasks. 10 | type Task struct { 11 | stop chan struct{} 12 | nextExecution time.Time 13 | startedAt time.Time 14 | stopped int32 // 0 means active, 1 means stopped 15 | once sync.Once 16 | } 17 | 18 | // newTask creates a new Task. 19 | func newTask() *Task { 20 | return &Task{ 21 | stop: make(chan struct{}), 22 | startedAt: time.Now(), 23 | } 24 | } 25 | 26 | // StartedAt returns the time when the scheduler was started. 27 | func (s *Task) StartedAt() time.Time { 28 | return s.startedAt 29 | } 30 | 31 | // NextExecutionTime returns the time when the next execution will happen. 32 | func (s *Task) NextExecutionTime() time.Time { 33 | return s.nextExecution 34 | } 35 | 36 | // ExecutesIn returns the duration until the next execution. 37 | func (s *Task) ExecutesIn() time.Duration { 38 | return time.Until(s.nextExecution) 39 | } 40 | 41 | // IsActive returns true if the scheduler is active. 42 | func (s *Task) IsActive() bool { 43 | return atomic.LoadInt32(&s.stopped) == 0 44 | } 45 | 46 | // Wait blocks until the scheduler is stopped. 47 | // After and At will stop automatically after the task is executed. 48 | func (s *Task) Wait() { 49 | <-s.stop 50 | } 51 | 52 | // Stop stops the scheduler. 53 | func (s *Task) Stop() { 54 | s.once.Do(func() { 55 | atomic.StoreInt32(&s.stopped, 1) 56 | close(s.stop) 57 | }) 58 | } 59 | 60 | // After executes the task after the given duration. 61 | // The function is non-blocking. If you want to wait for the task to be executed, use the Task.Wait method. 62 | func After(duration time.Duration, task func()) *Task { 63 | scheduler := newTask() 64 | scheduler.nextExecution = time.Now().Add(duration) 65 | timer := time.NewTimer(duration) 66 | 67 | go func() { 68 | select { 69 | case <-timer.C: 70 | task() 71 | scheduler.Stop() 72 | case <-scheduler.stop: 73 | // If the task is stopped before the timer fires, stop the timer. 74 | if !timer.Stop() { 75 | <-timer.C // drain if necessary 76 | } 77 | return 78 | } 79 | }() 80 | 81 | return scheduler 82 | } 83 | 84 | // At executes the task at the given time. 85 | // The function is non-blocking. If you want to wait for the task to be executed, use the Task.Wait method. 86 | func At(t time.Time, task func()) *Task { 87 | scheduler := newTask() 88 | scheduler.nextExecution = t 89 | d := time.Until(t) 90 | if d < 0 { 91 | d = 0 92 | } 93 | timer := time.NewTimer(d) 94 | 95 | go func() { 96 | select { 97 | case <-timer.C: 98 | task() 99 | scheduler.Stop() 100 | case <-scheduler.stop: 101 | if !timer.Stop() { 102 | <-timer.C 103 | } 104 | return 105 | } 106 | }() 107 | 108 | return scheduler 109 | } 110 | 111 | // Every executes the task in the given interval, as long as the task function returns true. 112 | // The function is non-blocking. If you want to wait for the task to be executed, use the Task.Wait method. 113 | func Every(interval time.Duration, task func() bool) *Task { 114 | scheduler := newTask() 115 | scheduler.nextExecution = time.Now().Add(interval) 116 | ticker := time.NewTicker(interval) 117 | 118 | go func() { 119 | for { 120 | select { 121 | case <-ticker.C: 122 | if !task() { 123 | scheduler.Stop() 124 | ticker.Stop() 125 | return 126 | } 127 | scheduler.nextExecution = time.Now().Add(interval) 128 | case <-scheduler.stop: 129 | ticker.Stop() 130 | return 131 | } 132 | } 133 | }() 134 | 135 | return scheduler 136 | } 137 | -------------------------------------------------------------------------------- /.github/workflows/atomicgo.yml: -------------------------------------------------------------------------------- 1 | # ┌───────────────────────────────────────────────────────────────────┐ 2 | # │ │ 3 | # │ IMPORTANT NOTE │ 4 | # │ │ 5 | # │ This file is synced with https://github.com/atomicgo/template │ 6 | # │ │ 7 | # │ Please apply all changes to the template repository │ 8 | # │ │ 9 | # └───────────────────────────────────────────────────────────────────┘ 10 | 11 | name: AtomicGo 12 | 13 | on: 14 | push: 15 | branches: 16 | - main 17 | 18 | permissions: 19 | contents: write 20 | packages: write 21 | 22 | jobs: 23 | test: 24 | name: Test Go Code 25 | runs-on: ${{ matrix.os }} 26 | strategy: 27 | matrix: 28 | os: [ubuntu-latest, windows-latest, macos-latest] 29 | steps: 30 | - name: Set up Go 31 | uses: actions/setup-go@v4 32 | with: 33 | go-version: stable 34 | 35 | - name: Check out code into the Go module directory 36 | uses: actions/checkout@v3 37 | 38 | - name: Get dependencies 39 | run: go get -v -t -d ./... 40 | 41 | - name: Build 42 | run: go build -v . 43 | 44 | - name: Test 45 | run: go test -coverprofile="coverage.txt" -covermode=atomic -v -p 1 . 46 | 47 | - name: Upload coverage to Codecov 48 | uses: codecov/codecov-action@v3 49 | 50 | build: 51 | name: Build AtomicGo Package 52 | runs-on: ubuntu-latest 53 | needs: test 54 | 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 57 | 58 | steps: 59 | - name: Checkout repository 60 | uses: actions/checkout@v3 61 | with: 62 | fetch-depth: 0 63 | 64 | - name: Download assets 65 | run: | 66 | mkdir -p .templates 67 | wget https://raw.githubusercontent.com/atomicgo/atomicgo/main/templates/example.gotxt -O .templates/example.gotxt 68 | wget https://raw.githubusercontent.com/atomicgo/atomicgo/main/templates/readme.md -O .templates/readme.md 69 | 70 | - name: Set up Go 71 | uses: actions/setup-go@v4 72 | with: 73 | go-version: stable 74 | 75 | - name: Install Go tools 76 | run: | 77 | go install github.com/robertkrimen/godocdown/godocdown@latest 78 | go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest 79 | go install github.com/caarlos0/svu@latest 80 | 81 | - name: Set up Git configuration 82 | run: | 83 | REPO_FULLNAME="${{ github.repository }}" 84 | echo "::group::Setup git" 85 | git config --global --add safe.directory /github/workspace 86 | 87 | echo "::notice::Login into git" 88 | git config --global user.email "git@marvinjwendt.com" 89 | git config --global user.name "MarvinJWendt" 90 | 91 | echo "::notice::Ignore workflow files (we may not touch them)" 92 | git update-index --assume-unchanged .github/workflows/* 93 | 94 | - name: Generate README.md 95 | run: | 96 | echo "::group::Generate README.md" 97 | FILE=./.github/atomicgo/custom_readme 98 | INCLUDE_UNEXPORTED=./.github/atomicgo/include_unexported 99 | if test -f "$FILE"; then 100 | echo "::notice::.github/custom_readme is present. Not generating a new readme." 101 | else 102 | echo "::notice::Running Godocdown" 103 | $(go env GOPATH)/bin/godocdown -template ./.templates/readme.md >README.md 104 | echo "::notice::Running gomarkdoc" 105 | GOMARKDOC_FLAGS="--template-file example=./.templates/example.gotxt" 106 | if test -f "$INCLUDE_UNEXPORTED"; then 107 | GOMARKDOC_FLAGS+=" -u" 108 | fi 109 | 110 | $(go env GOPATH)/bin/gomarkdoc $GOMARKDOC_FLAGS --repository.url "https://github.com/${{ github.repository }}" --repository.default-branch main --repository.path / -e -o README.md . 111 | fi 112 | echo "::endgroup::" 113 | 114 | - name: Run custom CI system 115 | run: | 116 | echo "::group::Run custom CI system" 117 | echo "::notice::Counting unit tests" 118 | unittest_count=$(go test -v -p 1 ./... | tee /dev/tty | grep -c "RUN") 119 | 120 | echo "::notice::Replacing badge in README.md" 121 | sed -i 's|> $GITHUB_ENV 148 | echo "::notice::Current version is $(svu current)" 149 | 150 | - name: Calculate next version 151 | id: next_version 152 | run: | 153 | echo "next_version=$(svu next)" >> $GITHUB_ENV 154 | echo "::notice::Next version is $(svu next)" 155 | 156 | - name: Check if release is needed 157 | id: check_release 158 | run: | 159 | echo "release_needed=$( [ '${{ env.current_version }}' != '${{ env.next_version }}' ] && echo true || echo false )" >> $GITHUB_ENV 160 | 161 | - name: Create tag 162 | if: env.release_needed == 'true' 163 | run: | 164 | git tag -a ${{ env.next_version }} -m "Release v${{ env.next_version }}" 165 | git push origin ${{ env.next_version }} 166 | sleep 5 # sleep for 5 seconds to allow GitHub to process the tag 167 | 168 | - name: Release 169 | if: env.release_needed == 'true' 170 | uses: softprops/action-gh-release@v2 171 | with: 172 | token: ${{ secrets.GITHUB_TOKEN }} 173 | generate_release_notes: true 174 | tag_name: ${{ env.next_version }} 175 | 176 | - name: Tweet release 177 | if: env.release_needed == 'true' && !github.event.repository.private 178 | uses: Eomm/why-don-t-you-tweet@v1 179 | with: 180 | tweet-message: 181 | "New ${{ github.event.repository.name }} release: ${{ env.next_version }} 🚀 182 | 183 | Try it out: atomicgo.dev/${{ github.event.repository.name }} 184 | 185 | #go #golang #opensource #library #release #atomicgo" 186 | env: 187 | TWITTER_CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }} 188 | TWITTER_CONSUMER_API_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }} 189 | TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }} 190 | TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

AtomicGo | schedule

2 | 3 |

4 | Downloads 5 | 6 | 7 | Latest Release 8 | 9 | 10 | 11 | Tests 12 | 13 | 14 | 15 | Coverage 16 | 17 | 18 | 19 | Unit test count 20 | 21 | 22 | 23 | License: MIT 24 | 25 | 26 | 27 | Go report 28 | 29 | 30 |

31 | 32 | --- 33 | 34 |

35 | Documentation 36 | | 37 | Contributing 38 | | 39 | Code of Conduct 40 |

41 | 42 | --- 43 | 44 |

45 | AtomicGo 46 |

47 | 48 |

49 | 50 | 51 | 52 |
53 |

54 |

go get atomicgo.dev/schedule

55 |

56 | 57 | 58 | 59 |
60 |

61 | 62 | 63 | 64 | 65 | 66 | # schedule 67 | 68 | ```go 69 | import "atomicgo.dev/schedule" 70 | ``` 71 | 72 | Package schedule provides a simple scheduler for Go. 73 | 74 | It can run a function at a given time, in a given duration, or repeatedly at a given interval. 75 | 76 | ## Index 77 | 78 | - [type Task](<#Task>) 79 | - [func After\(duration time.Duration, task func\(\)\) \*Task](<#After>) 80 | - [func At\(t time.Time, task func\(\)\) \*Task](<#At>) 81 | - [func Every\(interval time.Duration, task func\(\) bool\) \*Task](<#Every>) 82 | - [func \(s \*Task\) ExecutesIn\(\) time.Duration](<#Task.ExecutesIn>) 83 | - [func \(s \*Task\) IsActive\(\) bool](<#Task.IsActive>) 84 | - [func \(s \*Task\) NextExecutionTime\(\) time.Time](<#Task.NextExecutionTime>) 85 | - [func \(s \*Task\) StartedAt\(\) time.Time](<#Task.StartedAt>) 86 | - [func \(s \*Task\) Stop\(\)](<#Task.Stop>) 87 | - [func \(s \*Task\) Wait\(\)](<#Task.Wait>) 88 | 89 | 90 | 91 | ## type [Task]() 92 | 93 | Task holds information about the running task and can be used to stop running tasks. 94 | 95 | ```go 96 | type Task struct { 97 | // contains filtered or unexported fields 98 | } 99 | ``` 100 | 101 | 102 | ### func [After]() 103 | 104 | ```go 105 | func After(duration time.Duration, task func()) *Task 106 | ``` 107 | 108 | After executes the task after the given duration. The function is non\-blocking. If you want to wait for the task to be executed, use the Task.Wait method. 109 | 110 |
Example 111 |

112 | 113 | 114 | 115 | ```go 116 | package main 117 | 118 | import ( 119 | "fmt" 120 | "time" 121 | 122 | "atomicgo.dev/schedule" 123 | ) 124 | 125 | func main() { 126 | task := schedule.After(5*time.Second, func() { 127 | fmt.Println("5 seconds are over!") 128 | }) 129 | 130 | fmt.Println("Some stuff happening...") 131 | 132 | task.Wait() 133 | } 134 | ``` 135 | 136 |

137 |
138 | 139 | 140 | ### func [At]() 141 | 142 | ```go 143 | func At(t time.Time, task func()) *Task 144 | ``` 145 | 146 | At executes the task at the given time. The function is non\-blocking. If you want to wait for the task to be executed, use the Task.Wait method. 147 | 148 |
Example 149 |

150 | 151 | 152 | 153 | ```go 154 | package main 155 | 156 | import ( 157 | "fmt" 158 | "time" 159 | 160 | "atomicgo.dev/schedule" 161 | ) 162 | 163 | func main() { 164 | task := schedule.At(time.Now().Add(5*time.Second), func() { 165 | fmt.Println("5 seconds are over!") 166 | }) 167 | 168 | fmt.Println("Some stuff happening...") 169 | 170 | task.Wait() 171 | } 172 | ``` 173 | 174 |

175 |
176 | 177 | 178 | ### func [Every]() 179 | 180 | ```go 181 | func Every(interval time.Duration, task func() bool) *Task 182 | ``` 183 | 184 | Every executes the task in the given interval, as long as the task function returns true. The function is non\-blocking. If you want to wait for the task to be executed, use the Task.Wait method. 185 | 186 |
Example 187 |

188 | 189 | 190 | 191 | ```go 192 | package main 193 | 194 | import ( 195 | "fmt" 196 | "time" 197 | 198 | "atomicgo.dev/schedule" 199 | ) 200 | 201 | func main() { 202 | task := schedule.Every(time.Second, func() bool { 203 | fmt.Println("1 second is over!") 204 | 205 | return true // return false to stop the task 206 | }) 207 | 208 | fmt.Println("Some stuff happening...") 209 | 210 | time.Sleep(10 * time.Second) 211 | 212 | task.Stop() 213 | } 214 | ``` 215 | 216 |

217 |
218 | 219 | 220 | ### func \(\*Task\) [ExecutesIn]() 221 | 222 | ```go 223 | func (s *Task) ExecutesIn() time.Duration 224 | ``` 225 | 226 | ExecutesIn returns the duration until the next execution. 227 | 228 | 229 | ### func \(\*Task\) [IsActive]() 230 | 231 | ```go 232 | func (s *Task) IsActive() bool 233 | ``` 234 | 235 | IsActive returns true if the scheduler is active. 236 | 237 | 238 | ### func \(\*Task\) [NextExecutionTime]() 239 | 240 | ```go 241 | func (s *Task) NextExecutionTime() time.Time 242 | ``` 243 | 244 | NextExecutionTime returns the time when the next execution will happen. 245 | 246 | 247 | ### func \(\*Task\) [StartedAt]() 248 | 249 | ```go 250 | func (s *Task) StartedAt() time.Time 251 | ``` 252 | 253 | StartedAt returns the time when the scheduler was started. 254 | 255 | 256 | ### func \(\*Task\) [Stop]() 257 | 258 | ```go 259 | func (s *Task) Stop() 260 | ``` 261 | 262 | Stop stops the scheduler. 263 | 264 | 265 | ### func \(\*Task\) [Wait]() 266 | 267 | ```go 268 | func (s *Task) Wait() 269 | ``` 270 | 271 | Wait blocks until the scheduler is stopped. After and At will stop automatically after the task is executed. 272 | 273 | Generated by [gomarkdoc]() 274 | 275 | 276 | 277 | 278 | --- 279 | 280 | > [AtomicGo.dev](https://atomicgo.dev)  ·  281 | > with ❤️ by [@MarvinJWendt](https://github.com/MarvinJWendt) | 282 | > [MarvinJWendt.com](https://marvinjwendt.com) 283 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | # ┌───────────────────────────────────────────────────────────────────┐ 2 | # │ │ 3 | # │ IMPORTANT NOTE │ 4 | # │ │ 5 | # │ This file is synced with https://github.com/atomicgo/template │ 6 | # │ │ 7 | # │ Please apply all changes to the template repository │ 8 | # │ │ 9 | # └───────────────────────────────────────────────────────────────────┘ 10 | 11 | run: 12 | timeout: 3m 13 | 14 | linters: 15 | enable: 16 | - errcheck # check for unchecked errors 17 | - gosimple # specializes in simplifying code 18 | - govet # roughly the same as go vet 19 | - ineffassign # detects when assignments to existing variables are not used 20 | - staticcheck # staticcheck is a go vet on steroids, applying static analysis to your code 21 | - unused # finds unused variables and constants 22 | - asasalint # check `any` variadic funcs 23 | - asciicheck # check for non-ASCII characters 24 | - bidichk # check for dangerous unicode character sequences 25 | - bodyclose # check that HTTP response body is closed 26 | - canonicalheader # check that canonical headers are used 27 | - containedctx # detects struct contained context.Context field 28 | - contextcheck # check whether the function uses a non-inherited context 29 | - decorder # check declaration order and count of types, constants, variables and functions 30 | - dupl # finds duplicated code 31 | - durationcheck # check for two durations multiplied together 32 | - err113 # check the errors handling expressions 33 | - errchkjson # checks types passed to the json encoding functions 34 | - errname # check error names 35 | - errorlint # check error wrapping 36 | - exhaustive # check that all enum cases are handled 37 | - exportloopref # checks for pointers to enclosing loop variables 38 | - fatcontext # detects nested contexts in loops 39 | - forcetypeassert # finds unchecked type assertions 40 | - funlen # check for long functions 41 | - gci # controls Go package import order and makes it always deterministic 42 | - gocheckcompilerdirectives # checks that go compiler directive comments (//go:) are valid 43 | - gochecksumtype # exhaustiveness checks on Go "sum types" 44 | - gocognit # check for high cognitive complexity 45 | - gocritic # Go source code linter that provides a ton of rules 46 | - gocyclo # checks cyclomatic complexity 47 | - gofmt # checks whether code was gofmt-ed 48 | - gofumpt # checks whether code was gofumpt-ed 49 | - goimports # check import statements are formatted according to the 'goimport' command 50 | - goprintffuncname # checks that printf-like functions are named with f at the end 51 | - gosec # inspects source code for security problems 52 | - gosmopolitan # report certain i18n/l10n anti-patterns in your Go codebase 53 | - inamedparam # reports interfaces with unnamed method parameters 54 | - interfacebloat # check for large interfaces 55 | - intrange # find places where for loops could make use of an integer range 56 | - lll # check for long lines 57 | - maintidx # measures the maintainability index of each function 58 | - mirror # reports wrong mirror patterns of bytes/strings usage 59 | - misspell # finds commonly misspelled English words 60 | - musttag # enforce field tags in (un)marshaled structs 61 | - nakedret # checks that functions with naked returns are not longer than a maximum size 62 | - nestif # reports deeply nested if statements 63 | - nilerr # finds code that returns nil even if it checks that the error is not nil 64 | - nilnil # checks that there is no simultaneous return of nil error and an invalid value 65 | - nlreturn # checks for a new line before return and branch statements to increase code clarity 66 | - nolintlint # reports ill-formed or insufficient nolint directives 67 | - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL 68 | - paralleltest # detects missing usage of t.Parallel() method in your Go test 69 | - perfsprint # checks that fmt.Sprintf can be replaced with a faster alternative 70 | - prealloc # finds slice declarations that could potentially be pre-allocated 71 | - predeclared # finds code that shadows one of Go's predeclared identifiers 72 | - promlinter # checks Prometheus metrics naming via promlint 73 | - protogetter # reports direct reads from proto message fields when getters should be used 74 | - reassign # checks that package variables are not reassigned 75 | - revive # drop-in replacement of golint. 76 | - rowserrcheck # checks whether Rows.Err of rows is checked successfully 77 | - sloglint # ensures consistent code style when using log/slog 78 | - spancheck # checks for mistakes with OpenTelemetry/Census spans 79 | - sqlclosecheck # checks that sql.Rows, sql.Stmt, sqlx.NamedStmt, pgx.Query are closed 80 | - stylecheck # replacement for golint 81 | - tagalign # checks that struct tags are well aligned 82 | - tagliatelle # checks the struct tags 83 | - tenv # analyzer that detects using os.Setenv instead of t.Setenv 84 | - thelper # detects tests helpers which is not start with t.Helper() method 85 | - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes 86 | - unconvert # unnecessary type conversions 87 | - usestdlibvars # detects the possibility to use variables/constants from the Go standard library 88 | - varnamelen # checks that the length of a variable's name matches its scope 89 | - wastedassign # finds wasted assignment statements 90 | - whitespace # checks for unnecessary newlines at the start and end of functions, if, for, etc 91 | - wrapcheck # checks that errors returned from external packages are wrapped 92 | - wsl # add or remove empty lines 93 | 94 | disable: 95 | - copyloopvar # fixed in go 1.22+ 96 | - depguard # no forbidden imports 97 | - dogsled # blank identifiers are allowed 98 | - dupword # duplicate words are allowed 99 | - exhaustruct # many structs don't need to be exhaustive 100 | - forbidigo # no forbidden identifiers 101 | - ginkgolinter # not used 102 | - gochecknoinits # init functions are fine, if used carefully 103 | - goconst # many false positives 104 | - godot # comments don't need to be complete sentences 105 | - godox # todo comments are allowed 106 | - goheader # no need for a header 107 | - gomoddirectives # allow all directives 108 | - gomodguard # no forbidden imports 109 | - grouper # unused 110 | - importas # some aliases are fine 111 | - loggercheck # no slog support 112 | - makezero # make with non-zero initial length is fine 113 | - noctx # http request may be sent without context 114 | - nonamedreturns # named returns are fine 115 | - testableexamples # examples do not need to be testable (have declared output) 116 | - testifylint # testify is not recommended 117 | - testpackage # not a go best practice 118 | - unparam # interfaces can enforce parameters 119 | - zerologlint # slog should be used instead of zerlog 120 | - execinquery # deprecated 121 | - gomnd # deprecated 122 | - mnd # too many detections 123 | - cyclop # covered by gocyclo 124 | - gochecknoglobals # there are many valid reasons for global variables, depending on the project 125 | - ireturn # there are too many exceptions 126 | 127 | linters-settings: 128 | wsl: 129 | allow-cuddle-declarations: true 130 | force-err-cuddling: true 131 | force-case-trailing-whitespace: 3 132 | 133 | funlen: 134 | lines: 100 135 | statements: 50 136 | ignore-comments: true 137 | 138 | lll: 139 | line-length: 140 140 | tab-width: 1 141 | 142 | nlreturn: 143 | block-size: 2 144 | 145 | exhaustive: 146 | check-generated: false 147 | default-signifies-exhaustive: true 148 | 149 | varnamelen: 150 | ignore-type-assert-ok: true # ignore "ok" variables 151 | ignore-map-index-ok: true 152 | ignore-chan-recv-ok: true 153 | ignore-decls: 154 | - n int # generic number 155 | - x int # generic number (e.g. coordinate) 156 | - y int # generic number (e.g. coordinate) 157 | - z int # generic number (e.g. coordinate) 158 | - i int # generic number 159 | - a int # generic number 160 | - r int # generic number (e.g. red or radius) 161 | - g int # generic number (e.g. green) 162 | - b int # generic number (e.g. blue) 163 | - c int # generic number (e.g. count) 164 | - j int # generic number (e.g. index) 165 | - T any # generic type 166 | - a any # generic any (e.g. data) 167 | - b any # generic any (e.g. body) 168 | - c any # generic any 169 | - d any # generic any (e.g. data) 170 | - data any # generic data 171 | - n any # generic any 172 | - t time.Time # often used as a variable name 173 | - f func() # often used as a callback variable name 174 | - cb func() # often used as a callback variable name 175 | - t testing.T # default testing.T variable name 176 | - b testing.B # default testing.B variable name 177 | - sb strings.Builder # often used as a variable name 178 | 179 | issues: 180 | exclude-rules: 181 | - path: "_test(_[^/]+)?\\.go" 182 | linters: 183 | - gochecknoglobals 184 | - noctx 185 | - funlen 186 | - dupl 187 | --------------------------------------------------------------------------------