The best thing I can think of is to modify text/template to add futures support, and then do multi-pass rendering. The place to add this looks relatively simple, however the implementation looks complicated (and I just wrote my first program in go a couple weeks ago...)
The problem that I see with the solution below is that text/template execution tries to instantiate the values of each function serially and essentially waits for completion on each, serially. I've spent the last couple hours examining the text/template implementation. -- Michael On Wednesday, May 31, 2017 at 8:59:20 AM UTC-5, Egon wrote: > > The best idea I can think of (without digging and modifying text/template) > is to use special tokens and replace them afterwards... > Of course this approach has limitations in what it can do. > > *// note: code untested and incomplete* > > type Token string > type Queries struct { > pending sync.WaitGroup > mu sync.Mutex > responses map[Token]string > } > > func (q *Queries) createToken() Token { > return unique token > } > > func (q *Queries) Do(fn func() string) Token { > token := q.createToken() > q.pending.Add(1) > go func(){ > defer q.pending.Done() > result := fn() > q.mu.Lock() > q.responses[token] = result > q.mu.Unlock() > }() > return token > } > > func (q *Queries) Wait(){ q.pending.Wait() } > func (q *Queries) Patch(data []byte) []byte { > // replace tokens with responess > } > > func main() { > > q := NewQueries() > > var funcMap = template.FuncMap { > > "sleep": func() Token { return q.Do(func() string { time.Sleep(1 * > time.Second); return "slept" }) }, > > } > tmpl, _ := template.New("test").Funcs(funcMap).Parse("{{sleep}} > {{sleep}} {{sleep}}") > var buf bytes.Buffer > > tmpl.Execute(&buf, nil) > > q.Wait() > > os.Stdout.Write(q.Patch(buf.Bytes)) > > } > > The other approach would be to do multiple passes: > > 1. execute template > 2. collect funcs that haven't been run yet > 2.1. no funcs left --> output > 3. execute these funcs, cache the func values > 4. goto step 1 using the cache > > On Wednesday, 31 May 2017 16:26:15 UTC+3, Michael Brown wrote: >> >> I am designing a system that will heavily use text/template processing >> and I've run into one issue that is going to be a show stopper for me if I >> can't figure out a way around it. >> >> Execute() on a template will run all of the functions in the template >> serially. >> >> For example, when you run the code below, you can see it output "slept" >> once every second until it completes after 3 seconds. In this example, the >> sleep is simulating an RPC call to another process that may take some >> considerable time (few tenths of a second), but there will be a large >> number of these calls that could all theoretically run in parallel (ie. >> there are no data dependencies between them). I'd really like to know a way >> that I could have the templating engine run all of the functions at once >> and collect the output, ie. in the example below, the entire program should >> run in 1 second. >> >> package main >> >> >> import ( >> >> "text/template" >> >> "os" >> >> "time" >> >> ) >> >> >> var funcMap = template.FuncMap { >> >> "sleep": func() string { time.Sleep(1 * time.Second); return "slept" >> }, >> >> } >> >> >> func main() { >> >> tmpl, _ := template.New("test").Funcs(funcMap).Parse("{{sleep}} >> {{sleep}} {{sleep}}") >> >> tmpl.Execute(os.Stdout, nil) >> >> } >> >> >> -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.