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 [email protected].
For more options, visit https://groups.google.com/d/optout.