async

package module
v0.0.0-...-13b6eff Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 8, 2014 License: MIT Imports: 3 Imported by: 0

README

async

Build Status Coverage Status Total views Views in the last 24 hours authors

Simplistic asynchronous Go routines for the masses.

Documentation
Sourcegraph

License

Copyright (c) 2014 Colton Baker

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Documentation

Overview

Package async is a package to provide simplistic asynchronous routines for the masses.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Filter

func Filter(data interface{}, routine Routine, callbacks ...Done)

Filter allows you to filter out information from a slice in Waterfall mode.

You must call the Done function with false as its first argument if you do not want the data to be present in the results. No other arguments will affect the performance of this function. When calling the Done function, an error will cause the filtering to immediately exit.

For example, take a look at one of the tests for this function:

func TestFilterString(t *testing.T) {
  str := []string{
    "test1",
    "test2",
    "test3",
    "test4",
    "test5",
  }

  expects := []string{
    "test1",
    "test2",
    "test4",
    "test5",
  }

  mapper := func(done async.Done, args ...interface{}) {
    Status("Hit string")
    Status("Args: %+v\n", args)
    if args[0] == "test3" {
      done(nil, false)
      return
    }
    done(nil, true)
  }

  final := func(err error, results ...interface{}) {
    Status("Hit string end")
    Status("Results: %+v\n", results)
    for i := 0; i < len(results); i++ {
      if results[i] != expects[i] {
        t.Errorf("Did not filter correctly.")
        break
      }
    }
  }

  async.Filter(str, mapper, final)
}

Each Routine function will be passed the current value and its index the slice for its arguments.

func FilterParallel

func FilterParallel(data interface{}, routine Routine, callbacks ...Done)

FilterParallel allows you to filter out information from a slice in Parallel mode.

You must call the Done function with false as its first argument if you do not want the data to be present in the results. No other arguments will affect the performance of this function.

If there is an error, any further results will be discarded but it will not immediately exit. It will continue to run all of the other Routine functions that were passed into it. This is because by the time the error is sent, the goroutines have already been started. At this current time, there is no way to cancel a sleep timer in Go.

For example, take a look at one of the tests for this function:

func TestFilterStringParallel(t *testing.T) {
  str := []string{
    "test1",
    "test2",
    "test3",
    "test4",
    "test5",
  }

  expects := []string{
    "test1",
    "test2",
    "test4",
    "test5",
  }

  mapper := func(done async.Done, args ...interface{}) {
    Status("Hit string")
    Status("Args: %+v\n", args)
    if args[0] == "test3" {
      done(nil, false)
      return
    }
    done(nil, true)
  }

  final := func(err error, results ...interface{}) {
    Status("Hit string end")
    Status("Results: %+v\n", results)
    for i := 0; i < len(results); i++ {
      if results[i] != expects[i] {
        t.Errorf("Did not filter correctly.")
        break
      }
    }
  }

  async.FilterParallel(str, mapper, final)
}

Each Routine function will be passed the current value and its index the slice for its arguments.

The output of filtering in Parallel mode cannot be guaranteed to stay in the same order, due to the fact that it may take longer to process some things in your filter routine. If you need the data to stay in the order it is in, use Filter instead to ensure it stays in order.

func Map

func Map(data interface{}, routine Routine, callbacks ...Done)

Map allows you to manipulate data in a slice in Waterfall mode.

Each Routine will be called with the value and index of the current position in the slice. When calling the Done function, an error will cause the mapping to immediately exit. All other arguments are sent back as the replacement for the current value.

For example, take a look at one of the tests for this function:

func TestMapInt(t *testing.T) {
  ints := []int{1, 2, 3, 4, 5}

  expects := []int{2, 4, 6, 8, 10}

  mapper := func(done async.Done, args ...interface{}) {
    Status("Hit int")
    Status("Args: %+v\n", args)
    done(nil, args[0].(int)*2)
  }

  final := func(err error, results ...interface{}) {
    Status("Hit int end")
    Status("Results: %+v\n", results)
    for i := 0; i < len(results); i++ {
      if results[i] != expects[i] {
        t.Errorf("Did not map correctly.")
        break
      }
    }
  }

  async.Map(ints, mapper, final)
}

func MapParallel

func MapParallel(data interface{}, routine Routine, callbacks ...Done)

MapParallel allows you to manipulate data in a slice in Parallel mode.

Each Routine will be called with the value and index of the current position in the slice. When calling the Done function, arguments are sent back as the replacement for the current value.

If there is an error, any further results will be discarded but it will not immediately exit. It will continue to run all of the other Routine functions that were passed into it. This is because by the time the error is sent, the goroutines have already been started. At this current time, there is no way to cancel a sleep timer in Go.

For example, take a look at one of the tests for this function:

func TestMapStringParallel(t *testing.T) {
  str := []string{
    "test",
    "test2",
    "test3",
    "test4",
    "test5",
  }

  expects := []string{
    "test1",
    "test2",
    "test3",
    "test4",
    "test5",
  }

  mapper := func(done async.Done, args ...interface{}) {
    Status("Hit string")
    Status("Args: %+v\n", args)
    if args[1] == 0 {
      done(nil, "test1")
      return
    }
    done(nil, args[0])
  }

  final := func(err error, results ...interface{}) {
    Status("Hit string end")
    Status("Results: %+v\n", results)
    for i := 0; i < len(results); i++ {
      if results[i] != expects[i] {
        t.Errorf("Did not map correctly.")
        break
      }
    }
  }

  async.MapParallel(str, mapper, final)
}

The output of mapping in Parallel mode cannot be guaranteed to stay in the same order, due to the fact that it may take longer to process some things in your map routine. If you need the data to stay in the order it is in, use Map instead to ensure it stays in order.

func Parallel

func Parallel(routines []Routine, callbacks ...Done)

Parallel is a shorthand function to List.RunParallel without having to manually create a new list, add the routines, etc.

func Series

func Series(routines []Routine, callbacks ...Done)

Series is a shorthand function to List.RunSeries without having to manually create a new list, add the routines, etc.

func SeriesParallel

func SeriesParallel(routines []Routine, callbacks ...Done)

SeriesParallel is a shorthand function to List.RunSeriesParallel without having to manually create a new list, add the routines, etc.

func Waterfall

func Waterfall(routines []Routine, callbacks ...Done)

Waterfall is a shorthand function to List.RunWaterfall without having to manually create a new list, add the routines, etc.

Types

type Done

type Done func(error, ...interface{})

Done types are used for shorthand definitions of the functions that are passed into each Routine to show that the Routine has completed.

An example a Done function would be:

func ImDone(err error, args ...interface{}) {
  if err != nil {
    // Handle the error your Routine returned.
    return
  }

  // There wasn't an error returned your Routine! Do what you want with
  // the args.
}

type Event

type Event map[reflect.Value]int

Event is a map of functions to use for a single event. The integer is the frequency of calls that the function should make.

type Events

type Events map[string]Event

Events is a map that is used for containing everything related to the events. It stores the name of the event, functions for the event, and number of times the function should be called when the event is triggered.

All you need to do to create an event list is:

events := make(async.Events)

All event commands that return Events can be chained together. For example:

events.On("myevent", func() {
  println("Called myevent")
}).On("myevent2", func(msg string) {
  fmt.Printf("Called myevent2 with message: %s\n", msg)
}).Emit("myevent").Emit("myevent2", "Testing")

You can return an error from a function and it will be emitted as an error event. For example:

events.On("error", func(err error) {
  fmt.Printf("Error: %s", err)
}).On("myevent", func() error {
  return fmt.Errorf("Some error message")
}).Emit("myevent")

It's also easily inheritable by other structures. For example:

type MyStruct struct {
  async.Events
}

m := MyStruct{make(async.Events)}
m.On("myevent", func() {
  println("Called myevent")
}).Emit("myevent")

When you are defining your event, all function calls within that event must be in the same format.

For instance, this will work fine:

events.On("myevent", func() {}, func() {})

However, this will NOT work:

events.On("myevent", func() {}, func (msg string) {})

If you were to try this second example, and then try to .Emit("myevent", "message"), you would get an error from reflect saying that there is too many arguments for the first function. If you were to try .Emit("myevent"), you would get an error that there are not enough arguments because of the second function expecting an argument.

func (Events) Clear

func (e Events) Clear(name ...string) Events

Clear all events out of the event list. You can supply optional names for the events to be cleared.

For instance:

events.On("test", func() {}).On("test2", func() {}).Emit("test").Clear("test")

Returns the list of events for chaining commands.

func (Events) Emit

func (e Events) Emit(name string, args ...interface{}) Events

Emit an event. Arguments are optional. Each event will be ran as a Series.

For example:

events := make(async.Events)
events.On("myevent", func() {
  println("Emitted myevent")
})
events.Emit("myevent")

With arguments:

events := make(async.Events)
events.On("myevent", func(msg string) {
  fmt.Printf("Message: %s\n", msg)
})
events.Emit("myevent", "Testing")

Returns the list of events for chaining commands.

func (Events) Get

func (e Events) Get(name string) Event

Get Event map of functions and frequencies for the named event. This is just a convenience function. This data could also be accessed by the normal mapping methods.

For instance:

fmt.Printf("Events for myevent: %+v\n", e["myevent"])

func (Events) Length

func (e Events) Length(name string) int

Length gets the length of the Event map of functions and frequencies for the named event. This is just a convenience function. This data could also be accessed by the normal mapping methods.

For instance:

fmt.Printf("Length: %d", len(e["myevent"]))

func (Events) On

func (e Events) On(name string, callbacks ...interface{}) Events

On adds an event to be called forever.

This is equal to calling Times with -1 as the number of times to run the event. More documentation can be found on the Times function.

Returns the list of events for chaining commands.

func (Events) Once

func (e Events) Once(name string, callbacks ...interface{}) Events

Once adds an event to be called only once.

This is equal to calling Times with 1 as the number of times to run the event. More documentation can be found on the Times function.

Returns the list of events for chaining commands.

func (Events) Times

func (e Events) Times(name string, times int, callbacks ...interface{}) Events

Times adds an event to be called a number of times. If the number of times for the function to be called is -1, it will be called until the list is cleared.

Returns the list of events for chaining commands.

type List

type List struct {
	*list.List

	Wait sync.WaitGroup
}

List is used to contain the Routine functions to be processed

This list inherits https://godoc.org/container/list and contains all of the functionality that it contains, with a minor tweak to Remove. Instead of Remove returning the element, it returns our routine. This is used to ensure that our Routine is removed from the list before it's ran, and therefore isn't able to be called again.

func New

func New() *List

New will create a new List instance

func (*List) Add

func (l *List) Add(routine Routine) (*List, *list.Element)

Add will add a single Routine function to the current list

func (*List) Multiple

func (l *List) Multiple(routines ...Routine) (*List, []*list.Element)

Multiple will add multiple Routine functions to the current list

func (*List) Remove

func (l *List) Remove(element *list.Element) (*List, Routine)

Remove deletes a Routine element from the current list

An example of removing a Routine would be:

err, routine := l.Remove(l.Front())

func (*List) RunParallel

func (l *List) RunParallel(callbacks ...Done)

RunParallel will run all of the Routine functions from the current list in parallel mode.

All of the arguments returned in a Routine's Done function will be combined and returned in the callbacks that are provided.

If there is an error, any further results will be discarded but it will not immediately exit. It will continue to run all of the other Routine functions that were passed into it. This is because by the time the error is sent, the goroutines have already been started. At this current time, there is no way to cancel a sleep timer in Go.

For example:

async.Parallel([]async.Routine{
  func(done async.Done, args ...interface{}) {
    time.Sleep(20 * time.Second)
    done(nil, "Won't trigger the callbacks because error has been sent")
  },
  func(done async.Done, args ...interface{}) {
    done(fmt.Errorf("Test error"))
  }
}, func(err error, results ...interface{}) {
  if err != nil {
    fmt.Printf("Error: %s", err)
    return
  }

  fmt.Printf("Args: %s", args)
})

If you were to run this example, you would see the error happen immediately. However, you would also notice that the program doesn't immediately exit. That is because it is still waiting for responses that it silently discards, since an error has already occurred.

func (*List) RunSeries

func (l *List) RunSeries(callbacks ...Done)

RunSeries will run all of the Routine functions in a series effect.

If there is an error, series will immediately exit and trigger the callbacks with the error.

There are no arguments passed between the routines that are used in series. It is just for commands that need to run asynchronously without seeing the results of its previous routine.

For example, take a look at one of the tests for this function:

func TestSeries(t *testing.T) {
  counter := 0

  Status("Calling Series")
  async.Series([]async.Routine{
    func(done async.Done, args ...interface{}) {
      Status("Increasing counter...")
      counter++
      done(nil)
    },
    func(done async.Done, args ...interface{}) {
      Status("Increasing counter...")
      counter++
      done(nil)
    },
    func(done async.Done, args ...interface{}) {
      Status("Increasing counter...")
      counter++
      done(nil)
    },
    func(done async.Done, args ...interface{}) {
      Status("Increasing counter...")
      counter++
      done(nil)
    },
  }, func(err error, results ...interface{}) {
    if err != nil {
      t.Errorf("Unexpected error: %s", err)
      return
    }

    if counter != 4 {
      t.Errorf("Not all routines were completed.")
      return
    }

    Status("Counter: %d", counter)
  })
}

func (*List) RunSeriesParallel

func (l *List) RunSeriesParallel(callbacks ...Done)

RunSeriesParallel all of the Routine functions in a series effect, and in parallel mode.

If there is an error, any further results will be discarded but it will not immediately exit. It will continue to run all of the other Routine functions that were passed into it. This is because by the time the error is sent, the goroutines have already been started. At this current time, there is no way to cancel a sleep timer in Go.

There are no arguments passed between the routines that are used in series. It is just for commands that need to run asynchronously without seeing the results of its previous routine.

For example, take a look at one of the tests for this function:

func TestSeriesParallel(t *testing.T) {
  counter := 0

  Status("Calling Series")
  async.SeriesParallel([]async.Routine{
    func(done async.Done, args ...interface{}) {
      Status("Increasing counter...")
      counter++
      done(nil)
    },
    func(done async.Done, args ...interface{}) {
      Status("Increasing counter...")
      counter++
      done(nil)
    },
    func(done async.Done, args ...interface{}) {
      Status("Increasing counter...")
      counter++
      done(nil)
    },
    func(done async.Done, args ...interface{}) {
      Status("Increasing counter...")
      counter++
      done(nil)
    },
  }, func(err error, results ...interface{}) {
    if err != nil {
      t.Errorf("Unexpected error: %s", err)
      return
    }

    if counter != 4 {
      t.Errorf("Not all routines were completed.")
      return
    }

    Status("Counter: %d", counter)
  })
}

func (*List) RunWaterfall

func (l *List) RunWaterfall(callbacks ...Done)

RunWaterfall runs all of the Routine functions in a waterfall effect.

The arguments of the previous Routine function will be passed into the next Routine function. The final result provided to the callbacks will be the result of the last Routine function.

If there is an error, waterfall will immediately exit and trigger the callbacks with the error.

type Routine

type Routine func(Done, ...interface{})

Routine types are used for shorthand definitions of the functions that are actually ran when calling Parallel, Waterfall, etc.

An example of a Routine function would be:

func MyRoutine(done async.Done, args ...interface{}) {
  // Do something in your routine and then call its done function.
  done(nil, "arg1", "arg2", "arg3")
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL