# 示例 · gotick 文档

> 下面三种形状反复出现。每一段都完整到可以贴进项目里再改。

文档

- [01快速上手](/docs.md)
- [02重放模型](/docs/model.md)
- [03底层实现](/docs/internals.md)
- [04核心 API](/docs/primitives.md)
- [05外部控制](/docs/control.md)
- [06示例](/docs/examples.md)
- [07运维与排查](/docs/operations.md)
- [08已知局限](/docs/limits.md)

# 三个常见场景怎么写

下面三种形状反复出现。每一段都完整到可以贴进项目里再改。

本页目录

- [事件和超时赛跑](#race)
- [散开，再收回来](#fanout)
- [只有最后一次改动算数](#debounce)

下面的例子省掉了 createOrder、download 这类辅助函数，要看的是流程的形状。

## 事件和超时赛跑

三十分钟内没付款就关单。难的从来不是定时器，是支付和超时会撞上：拆成两个流程互相取消，这场竞态得你自己赢。这里胜负由一次原子操作定死，29:59.9 落地的那笔钱要么被采纳、要么被拒，不会两边都成立。

复制

```
tick.Flow("order/close", func(ctx *gotick.Context) {    orderId, _ := ctx.MetaData("order_id")     gotick.Task(ctx, "create-order", func(c *gotick.TaskContext) error {        return orders.Create(c, orderId)    })     paid, ok := gotick.WaitForSignal[Payment](ctx, "paid",        gotick.WithSignalTimeout(30*time.Minute))     if !ok {        gotick.Task(ctx, "close-order", func(c *gotick.TaskContext) error {            return orders.Close(c, orderId)        })        return    }     gotick.Task(ctx, "ship", func(c *gotick.TaskContext) error {        return shipping.Send(c, orderId, paid.TradeNo)    })})
```

## 散开，再收回来

下载一批文件，最多四个同时进行。Array 记住列表，每一遍对「要遍历什么」的答案都一样。AsyncArray 把列表展开成并行步骤，Wait 等它们做完。每个元素有自己的状态，重启之后只接着做没做完的那些。

复制

```
tick.Flow("media/convert", func(ctx *gotick.Context) {    dir, _ := ctx.MetaData("dir")     files := gotick.Array(ctx, "list", func(c *gotick.TaskContext) ([]string, error) {        return storage.List(c, dir)    })     fs := gotick.AsyncArray(ctx, "convert", files,        func(c *gotick.TaskContext, name string, i int) (string, error) {            return ffmpeg.Convert(c, name)        })     gotick.Wait(ctx, 4, fs...)     gotick.Task(ctx, "publish", func(c *gotick.TaskContext) error {        out := make([]string, 0, len(fs))        for _, f := range fs {            out = append(out, f.(*gotick.FutureT[string]).Value())        }        return catalog.Publish(c, dir, out)    })})
```

## 只有最后一次改动算数

用户一分钟内改了同一份文档十次，每次都触发一轮很贵的重新生成，但只有最后一次的结果会被人看到。用文档 ID 给流程做 key，前九次自己就取消了，不用再为没人看的结果付钱。

复制

```
tick.Flow("doc/preview", func(ctx *gotick.Context) {    docId, _ := ctx.MetaData("doc_id")     // expensive, and only the newest result will ever be looked at    url := gotick.Memo(ctx, "render", func() (string, error) {        return renderer.Build(docId)    })     gotick.Task(ctx, "attach", func(c *gotick.TaskContext) error {        return docs.SetPreview(c, docId, url)    })}) // in the save handler — ten edits, nine of them canceled automaticallytick.Trigger(ctx, "doc/preview", gotick.MetaData{"doc_id": docId},    gotick.WithKey(docId))
```

更多能直接跑的例子在仓库的 example 目录里，包括上面这个关单的例子，接上真的 Redis 几秒钟就能看到结果。 [example/](https://github.com/zbysir/gotick/tree/master/example)

[← 上一篇\
\
外部控制](/docs/control.md) [下一篇 →\
\
运维与排查](/docs/operations.md)

本页目录

- [事件和超时赛跑](#race)
- [散开，再收回来](#fanout)
- [只有最后一次改动算数](#debounce)

> 全站页面清单：[/llms.txt](/llms.txt)
