This is almost certainly going to be a case where you have the correct solution and I don't have the go experience to properly understand it. I wasn't quite understanding how your code was "patching" the results and how the template package knows to wait for it. Let me describe my problem.
I have a REST interface running on an embedded system. The REST api is mandated by another entity and I have zero control over the output, I have to produce specific conformant output. The data that I need to build the interface is on the system in a variety of other processes, and I can get that data via a couple of different RPC mechanisms, however sometimes these RPC mechanisms can be slow. (I'm in progress building go bindings for them). The current code which creates the REST interface is a huge morass of C code that generates the JSON output, so the exact structure of the JSON output is hardcoded in the code. I have an opportunity here, and that is that it appears to me that, with a little work, I can completely templatize the rest output. That is, I can have zero page-specific code and render almost all of the output by providing a small number of generic data access functions. The issue I ran into is the serial nature of the substitution kills the performance in my prototype. Yes, I can make a specific go function per page that gathers all the data I need and provides it to the template. But that would mean that I'd need to maintain both the templates and the data access functions. This is still an improvement on the old way of doing things, but I was hoping to jump straight to a fully templatized system, which according to my initial analysis, would be considerably less code and would not really be abusing the template system (most of the function calls are very straightforward and there is no heavy processing of the output). -- Michael On Wednesday, May 31, 2017 at 10:56:57 AM UTC-5, Egon wrote: > > Both of my described approaches run the funcs serially however it does not > wait for the response and later patxhes the results. > > Can you describe the whole thing you are building? Piecing the > requirements and purpose together from comments is difficult. > > I.e. how much memory, how big is request latency, who gets the output, > where do the templates come from... > > On May 31, 2017 6:40 PM, "Michael Brown" <michael...@gmail.com > <javascript:>> wrote: > > 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 a topic in the > Google Groups "golang-nuts" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/golang-nuts/j0WwjQE11rw/unsubscribe. > To unsubscribe from this group and all its topics, send an email to > golang-nuts...@googlegroups.com <javascript:>. > For more options, visit https://groups.google.com/d/optout. > > > -- 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.