Yang MingHui
Yang MingHui

Reputation: 400

how to execute ticker immediately when function start to run in golang

I would hope to execute the "weekly updated" before "daily check" as follows. That means "time.Time" should put "timeChan" immediately rather than do it after waiting for over two seconds when the main function Start running.

And the result should be like this

weekly updated
daily check
daily check
daily check
daily check
weekly updated
daily check
daily check
daily check
daily check
...

Of course,i can just print "weekly updated" firstly one time, but there is have an elegant method?

The code is as follows

package main

import "time"
import "fmt"

func main() {
    var a int
    timeChan := time.NewTicker(time.Second * 2).C

    tickChan := time.NewTicker(time.Millisecond * 500).C

    for {
        select {
        case <-timeChan:
            fmt.Println("weekly updated")
            a = 1
        case <-tickChan:
            if a == 1 {
                fmt.Println("daily check")
            } else {
                fmt.Println("Not update?")
            }
        }
    }
}

The result is as follows

Not update?
Not update?
Not update?
weekly updated
daily check
daily check
daily check
daily check
weekly updated
daily check
daily check
daily check
daily check
...

Upvotes: 2

Views: 5393

Answers (4)

smallyu
smallyu

Reputation: 51

Use a proxy channel?

package main

import (
    "fmt"
    "time"
)

func main() {
    var a int
    timeChan := time.NewTicker(time.Second * 2).C

    tickChan := time.NewTicker(time.Millisecond * 500).C

    // use a proxy channel
    proxyChan := make(chan int)
    go func() {
        // execute the proxy channel immediately
        proxyChan <- 0
        for {
            select {
            case <-timeChan:
                proxyChan <- 0
            }
        }
    }()

    for {
        select {
        case <-proxyChan: // use proxyChan rather than timeChan
            fmt.Println("weekly updated")
            a = 1
        case <-tickChan:
            if a == 1 {
                fmt.Println("daily check")
            } else {
                fmt.Println("Not update?")
            }
        }
    }
}

And you can abstract it like this:

package main

import (
    "fmt"
    "time"
)

func main() {
    var a int
    // use like this
    timeChan, callback := NewProxyTicker(time.Second * 2)
    go callback()

    tickChan := time.NewTicker(time.Millisecond * 500).C

    for {
        select {
        case <-timeChan: 
            fmt.Println("weekly updated")
            a = 1
        case <-tickChan:
            if a == 1 {
                fmt.Println("daily check")
            } else {
                fmt.Println("Not update?")
            }
        }
    }
}

func NewProxyTicker(d time.Duration) (chan time.Time, func()) {
    ticker := time.NewTicker(d)
    proxyChan := make(chan time.Time)
    callback := func() {
        proxyChan <- time.Now()
        for {
            select {
            case <-ticker.C:
                proxyChan <- time.Now()
            }
        }
    }
    return proxyChan, callback
}

Upvotes: 1

Modulator
Modulator

Reputation: 1

If the purpose of the for statement is to only trigger that one function/task at an interval, you can place the work logic before the select statement and then use the cases to trigger the for loop to cycle. This will cause the work logic to run on entry into the loop.

I don't know the structure of your team's project, so if your select statement has many other cases not meant as triggers, then this wouldn't work.

package main

import (
    "context"
    "log"
    "os"
    "os/signal"
    "time"
)

func main() {
    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
    defer cancel()

    weeklyTicker := time.NewTicker(2 * time.Second)
    dailyTicker := time.NewTicker(10 * time.Second)
    customTrigger := make(chan struct{})

    // Example of custom trigger at a random point
    go func() {
        time.Sleep(5 * time.Second)
        customTrigger <- struct{}{}
    }()

    weeklyRun := true
    for {
        if weeklyRun {
            log.Println("Run weekly logic")
            weeklyRun = false
        } else {
            log.Println("Run daily logic")
        }

        select {
        case <-ctx.Done():
            log.Println("interrupt received")
            return
        case <-dailyTicker.C:
            log.Println("weekly updated")
            weeklyRun = true
        case <-weeklyTicker.C:
            log.Println("daily check")
        case <-customTrigger:
            log.Println("custom check trigger")
        }
    }
}

Output:

2022/12/22 13:52:12 Run weekly logic
2022/12/22 13:52:14 daily check
2022/12/22 13:52:14 Run daily logic
2022/12/22 13:52:16 daily check
2022/12/22 13:52:16 Run daily logic
2022/12/22 13:52:17 custom check trigger
2022/12/22 13:52:17 Run daily logic
2022/12/22 13:52:18 daily check
2022/12/22 13:52:18 Run daily logic
2022/12/22 13:52:20 daily check
2022/12/22 13:52:20 Run daily logic
2022/12/22 13:52:22 daily check
2022/12/22 13:52:22 Run daily logic
2022/12/22 13:52:22 weekly updated
2022/12/22 13:52:22 Run weekly logic
^C2022/12/22 13:52:23 interrupt received

Upvotes: 0

Adrian
Adrian

Reputation: 46403

Just put the work in a function and call it.

var a int
timeChan := time.NewTicker(time.Second * 2).C
tickChan := time.NewTicker(time.Millisecond * 500).C
f := func() {
    fmt.Println("weekly updated")
    a = 1
}
f()
for {
    select {
    case <-timeChan:
        f()
    case <-tickChan:
        if a == 1 {
            fmt.Println("daily check")
        } else {
            fmt.Println("Not update?")
        }
    }
}

Upvotes: 1

Shahriar
Shahriar

Reputation: 13806

Set your Ticker for weekly at first time.Millisecond. Then change it, when 1st time it is done.

package main

import (
    "fmt"
    "time"
)

func main() {
    var a = 0

    ticker := time.NewTicker(1)
    timeChan := ticker.C
    tickChan := time.NewTicker(time.Millisecond * 500).C

    for {
        select {
        case <-timeChan:
            fmt.Println("weekly updated")
            if a == 0 {
                ticker.Stop()
                timeChan = time.NewTicker(time.Second * 2).C
            }
            a = 1
        case <-tickChan:
            if a == 1 {
                fmt.Println("daily check")
            } else {
                fmt.Println("Not update?")
            }
        default:
        }
    }
}

Output:

weekly updated
daily check
daily check
daily check
daily check
weekly updated

Upvotes: 3

Related Questions