Hmm… I need to come back to this idea of a design pattern that resembles a plugable motor function.

Playground

package main

import (
	"context"
	"log"
	"sync"
	"time"
)

type AA func(context.Context, string, string, int) (int, error)

func Varoom(ctx context.Context, s0 string, sE string, t int) (int, error) {

	gen := func(ctx context.Context) <-chan string {
		dst := make(chan string)
		var output string
		go func() {
		        // Important to close dst
		        defer close(dst)
			for {
				output += s0 + "," + sE

				select {

				case <-ctx.Done():
					log.Printf("ctx.Done() in Loop")
					dst <- "done"
					return // returning not to leak the goroutine
				case dst <- output:
					time.Sleep(time.Duration(t) * time.Second)
				}

			}
		}()
		return dst
	}

	for output := range gen(ctx) {

		log.Printf("output: %s\n", output)

		if ctx.Err() != nil {
			log.Printf("calling break")
			break
		}

	}

	return 1, nil

}

type A struct {
	sync.Mutex
	S  string
	I  int
	D  int
	AA AA
}

func (a *A) PlugIn(aa AA) {
	a.AA = aa
}

func (a *A) RunAA(s string) (int, error) {
	a.Lock()
	defer a.Unlock()

	ctx, cancel := context.WithTimeout(context.Background(),
		time.Duration(time.Duration(a.D)*time.Second))
	defer cancel()

	if a.AA == nil {
		return -1, nil
	}

	return a.AA(ctx, a.S, s, a.I)
}

func main() {

	a := A{}
	a.S = "a"
	a.I = 1
	a.D = 5
	a.PlugIn(Varoom)
	a.RunAA("b")

}

Other examples..

playground

package main

import (
	"bytes"
	"fmt"
	"os"
)

func main() {

	var stdoutBuff bytes.Buffer
	defer stdoutBuff.WriteTo(os.Stdout)

	intStream := make(chan int, 5)

	go func() {
		defer close(intStream)
		defer fmt.Fprintln(&stdoutBuff, "Producer Done.")

		for i := 0; i < 5; i++ {
			fmt.Fprintf(&stdoutBuff, "Sending: %d\n", i)
			intStream <- i
		}

	}()

	for i := range intStream {
		fmt.Fprintf(&stdoutBuff, "Received %v\n", i)
	}
}