github.com/searKing/golang/go@v1.2.117/go/generator/generator.go (about) 1 // Copyright 2020 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package generator 6 7 import ( 8 "context" 9 ) 10 11 type Yield func(msg any) (ok bool) 12 13 // Generator behaves like Generator in python or ES6 14 // Generator function contains one or more yield statement. 15 // Generator functions allow you to declare a function that behaves like an iterator, i.e. it can be used in a for loop. 16 // see https://wiki.python.org/moin/Generators 17 // see https://www.programiz.com/python-programming/generator 18 // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* 19 type Generator struct { 20 // Used by Next, to notify or deliver what is generated, as next in python or ES6 21 C <-chan any 22 23 r runtimeGenerator 24 } 25 26 // Stop prevents the Generator from firing, with the channel drained. 27 // Stop ensures the channel is empty after a call to Stop. 28 // It returns true if the call stops the generator, false if the generator has already 29 // expired or been stopped. 30 // Stop does not close the channel, to prevent a read from the channel succeeding 31 // incorrectly. 32 // 33 // This cannot be done concurrent to other receives from the Generator's 34 // channel. 35 // 36 // For a generator created with GeneratorFuncWithSupplier(supplierC, f), if t.Stop returns false, then the generator 37 // has already expired and the function f has been started in its own goroutine; 38 // Stop does not wait for f to complete before returning. 39 // If the caller needs to know whether f is completed, it must coordinate 40 // with f explicitly. 41 func (g *Generator) Stop() bool { 42 if g.r.f == nil || g.r.ctx == nil || g.r.cancel == nil { 43 panic("generator: Stop called on uninitialized Generator") 44 } 45 return g.r.stop() 46 } 47 48 func (g *Generator) StoppedC() context.Context { 49 if g.r.f == nil || g.r.ctx == nil || g.r.cancel == nil { 50 panic("generator: StoppedC called on uninitialized Generator") 51 } 52 return g.r.ctx 53 } 54 55 func (g *Generator) Stopped() bool { 56 if g.r.f == nil || g.r.ctx == nil || g.r.cancel == nil { 57 panic("generator: Stopped called on uninitialized Generator") 58 } 59 return g.r.stopped() 60 } 61 62 // Next behaves like an iterator, i.e. it can be used in a for loop. 63 // It's a grammar sugar for chan 64 func (g *Generator) Next() (msg any, ok bool) { 65 msg, ok = <-g.C 66 return 67 } 68 69 // Yield is a grammar sugar for data src of generator 70 // ok returns true if msg sent; false if consume canceled 71 // If a function contains at least one yield statement (it may contain other yield or return statements), 72 // it becomes a generator function. Both yield and return will return some value from a function. 73 // The difference is that, while a return statement terminates a function entirely, 74 // yield statement pauses the function saving all its states and later continues from there on successive calls. 75 func (g *Generator) Yield(supplierC chan<- any) Yield { 76 return func(msg any) (ok bool) { 77 select { 78 case <-g.StoppedC().Done(): 79 return false 80 case supplierC <- msg: 81 return true 82 } 83 } 84 } 85 86 // Simply speaking, a generator is a function that returns an object (iterator) which we can iterate over (one value at a time). 87 88 // GeneratorFunc returns an object (iterator) which we can iterate over (one value at a time). 89 // It returns a Generator that can be used to cancel the call using its Stop method. 90 // Iterate will be stopped when f is return or Stop is called. 91 func GeneratorFunc(f func(yield Yield)) *Generator { 92 supplierC := make(chan any) 93 g := GeneratorWithSupplier(supplierC) 94 supplierF := func(args ...any) { 95 yield := g.Yield(supplierC) 96 f(yield) 97 close(supplierC) 98 } 99 go supplierF() 100 return g 101 } 102 103 // GeneratorWithSupplier is like GeneratorFunc. 104 // But it's data src is from supplierC. 105 // Iterate will be stopped when supplierC is closed or Stop is called. 106 func GeneratorWithSupplier(supplierC <-chan any) *Generator { 107 c := make(chan any) 108 109 ctx, cancel := context.WithCancel(context.Background()) 110 g := &Generator{ 111 C: c, 112 r: runtimeGenerator{ 113 f: sendChan, 114 arg: c, 115 ctx: ctx, 116 cancel: cancel, 117 supplierC: supplierC, 118 consumerC: c, 119 }, 120 } 121 g.r.start() 122 return g 123 } 124 125 // Deprecated: Use GeneratorFunc wrapped by closure instead. 126 func GeneratorVariadicFunc(f func(yield Yield, args ...any)) func(args ...any) *Generator { 127 supplierC := make(chan any) 128 g := GeneratorWithSupplier(supplierC) 129 supplierF := func(args ...any) { 130 yield := g.Yield(supplierC) 131 f(yield, args...) 132 close(supplierC) 133 } 134 return func(args ...any) *Generator { 135 go supplierF(args...) 136 return g 137 } 138 } 139 140 // GeneratorFuncWithSupplier waits for the supplierC to supply and then calls f 141 // in its own goroutine every time. It returns a Generator that can 142 // be used to cancel the call using its Stop method. 143 // Consume will be stopped when supplierC is closed. 144 func GeneratorFuncWithSupplier(supplierC <-chan any, f func(msg any)) *Generator { 145 ctx, cancel := context.WithCancel(context.Background()) 146 g := &Generator{ 147 r: runtimeGenerator{ 148 f: goFunc, 149 arg: f, 150 ctx: ctx, 151 cancel: cancel, 152 supplierC: supplierC, 153 }, 154 } 155 g.r.start() 156 return g 157 158 } 159 160 func sendChan(ctx context.Context, c any, msg any) { 161 // Blocking send of msg on c. 162 select { 163 case <-ctx.Done(): 164 return 165 case c.(chan any) <- msg: 166 } 167 return 168 } 169 170 // arg -> func 171 func goFunc(ctx context.Context, f any, msg any) { 172 go f.(func(any))(msg) 173 }