future

package module
v0.0.0-...-a898d89 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2019 License: Apache-2.0 Imports: 2 Imported by: 0

README

** Capital One built this project to help our engineers as well as users in the community. We are no longer able to fully support the project. We have archived the project as of Jul 9 2019 where it will be available in a read-only state. Feel free to fork the project and maintain your own version. **

Go Report Card Sourcegraph

future

A simple Future (Promise) library for Go.

Usage

Basic usage (wait forever):

package main

import (
  "fmt"
  future "github.com/capitalone/go-future-context"
)

func ThingThatTakesALongTimeToCalculate(inVal int) (string, error) {
  //this does something but it's not that important
  return "Hello", nil
}

func main() {
  inVal := 200
  f := future.New(func() (interface{}, error) {
    return ThingThatTakesALongTimeToCalculate(inVal)
  })
  
  result, err := f.Get()
  fmt.Println(result, err)
}

Timeout usage (wait for specified amount of time):

package main

import (
	"fmt"
	future "github.com/capitalone/go-future-context"
	"time"
)

func ThingThatTakesALongTimeToCalculate(inVal int) (string, error) {
  //this does something but it's not that important
  return "Hello", nil
}

func main() {
  inVal := 200
  f := future.New(func() (interface{}, error) {
    return ThingThatTakesALongTimeToCalculate(inVal)
  })
  
  result, timeout, err := f.GetUntil(5 * time.Second)
  fmt.Println(result, timeout, err)
}

timeout will be true if the timeout was triggered.

  • The future methods Get and GetUntil can be called multiple times.
  • Once the value is calculated, the same value (and error) will be returned immediately.

Chaining usage (invoke a next method if the Promise doesn't return an error):

package main

import (
	"fmt"
	future "github.com/capitalone/go-future-context"
	"time"
)

func ThingThatTakesALongTimeToCalculate(inVal int) (int, error) {
	//this does something but it's not that important
	time.Sleep(5 * time.Second)
	return inVal * 2, nil
}

func main() {
	inVal := 200
	f := future.New(func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	}).Then(func(i interface{}) (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(i.(int))
	})

    result, err := f.Get()
    fmt.Println(result, err)
}

You can use Then to chain together as many promises as you want.

  • If an error is returned at any step along the way, the rest of the calls in the promise chain are skipped.
  • Only the value and error of the last executed promise is returned; all others are lost.

Cancellation (stop waiting for a Get or GetUntil to complete):

package main

import (
	"fmt"
	future "github.com/capitalone/go-future-context"
	"time"
)

func ThingThatTakesALongTimeToCalculate(inVal int) (int, error) {
	//this does something but it's not that important
	time.Sleep(5 * time.Second)
	return inVal * 2, nil
}

func timeIt(f func()) int64 {
	start := time.Now()
	f()
	end := time.Now()
	dur := end.Unix() - start.Unix()
	return dur
}

func main() {
	inVal := 200
	f := future.New(func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	})

	go func() {
		time.Sleep(2 * time.Second)
		f.Cancel()
	}()
	fmt.Println(timeIt(func() {
		result, err := f.Get()
		fmt.Println(result, err, f.IsCancelled())
	}))
}
  • Calling Cancel after a Get or GetUntil has completed has no effect.
  • Calling Cancel multiple times has no effect.
  • When a future is cancelled, the process continues in the background but any data returned is not accessible.
  • If GetUntil returns due to a timeout, it does not cancel the future. If you wish to cancel based on a GetUntil timeout, do the following:
	f := future.New(func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	})
    val, timeout, err := f.GetUntil(2000)
    if timeout {
        f.Cancel()
    }

Context support: Future contains support for the Context interface included in Go 1.7:

package main

import (
	"fmt"
	future "github.com/capitalone/go-future-context"
	"time"
	"context"
)

func ThingThatTakesALongTimeToCalculate(inVal int) (int, error) {
	//this does something but it's not that important
	time.Sleep(5 * time.Second)
	return inVal * 2, nil
}

func timeIt(f func()) int64 {
	start := time.Now()
	f()
	end := time.Now()
	dur := end.Unix() - start.Unix()
	return dur
}

func main() {
	inVal := 200
	ctx, _ := context.WithTimeout(context.Background(),2*time.Second)

	f := future.NewWithContext(ctx, func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	})

	fmt.Println(timeIt(func() {
		result, err := f.Get()
		fmt.Println(result, err, f.IsCancelled())
	}))
}

When a future is created using NewWithContext, it is cancelled when the Context's Done channel is closed, whether it is closed due to timeout or an explicit call to the CancelFunc returned by the Context factory functions.

Contributors:

We welcome your interest in Capital One’s Open Source Projects (the “Project”). Any Contributor to the project must accept and sign a CLA indicating agreement to the license terms. Except for the license granted in this CLA to Capital One and to recipients of software distributed by Capital One, you reserve all right, title, and interest in and to your contributions; this CLA does not impact your rights to use your own contributions for any other purpose.

Link to Individual CLA

Link to Corporate CLA

This project adheres to the Open Source Code of Conduct. By participating, you are expected to honor this code.

Documentation

Overview

Example (Cancel)
package main

import (
	"fmt"
	future "github.com/capitalone/go-future-context"
	"time"
)

func main() {
	inVal := 200

	ThingThatTakesALongTimeToCalculate := func(inVal int) (int, error) {
		//this does something but it's not that important
		time.Sleep(5 * time.Second)
		return inVal * 2, nil
	}

	f := future.New(func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	})

	go func() {
		time.Sleep(2 * time.Second)
		f.Cancel()
	}()

	result, err := f.Get()
	fmt.Println(result, err, f.IsCancelled())

}
Output:
<nil> <nil> true
Example (Context)
package main

import (
	"context"
	"fmt"
	"time"

	future "github.com/capitalone/go-future-context"
)

func main() {
	inVal := 200

	ThingThatTakesALongTimeToCalculate := func(inVal int) (int, error) {
		//this does something but it's not that important
		time.Sleep(5 * time.Second)
		return inVal * 2, nil
	}

	ctx, cancelFunc := context.WithTimeout(context.Background(), 2*time.Second)

	f := future.NewWithContext(ctx, func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	})

	result, err := f.Get()
	fmt.Println(result, err, f.IsCancelled())
	cancelFunc()

}
Output:
<nil> <nil> true
Example (Get)
package main

import (
	"fmt"
	future "github.com/capitalone/go-future-context"
	"time"
)

func main() {
	inVal := 200

	ThingThatTakesALongTimeToCalculate := func(inVal int) (int, error) {
		//this does something but it's not that important
		time.Sleep(5 * time.Second)
		return inVal * 2, nil
	}

	f := future.New(func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	})

	result, err := f.Get()
	fmt.Println(result, err)

	// second call, results are instantaneous
	result, err = f.Get()
	fmt.Println(result, err)

}
Output:
400 <nil>
400 <nil>
Example (GetUntil)
package main

import (
	"fmt"
	"time"

	future "github.com/capitalone/go-future-context"
)

func main() {
	inVal := 200

	ThingThatTakesALongTimeToCalculate := func(inVal int) (int, error) {
		//this does something but it's not that important
		time.Sleep(5 * time.Second)
		return inVal * 2, nil
	}

	f := future.New(func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	})

	// times out
	result, timeout, err := f.GetUntil(2 * time.Second)
	fmt.Println(result, timeout, err)

	// completes before 10s passes
	result, timeout, err = f.GetUntil(10 * time.Second)
	fmt.Println(result, timeout, err)

	//  results are instantaneous
	result, timeout, err = f.GetUntil(10 * time.Second)
	fmt.Println(result, timeout, err)

}
Output:
<nil> true <nil>
400 false <nil>
400 false <nil>
Example (Then)
package main

import (
	"fmt"
	future "github.com/capitalone/go-future-context"
	"time"
)

func main() {
	inVal := 200

	ThingThatTakesALongTimeToCalculate := func(inVal int) (int, error) {
		//this does something but it's not that important
		time.Sleep(5 * time.Second)
		return inVal * 2, nil
	}

	f := future.New(func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	}).Then(func(i interface{}) (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(i.(int))
	})

	result, err := f.Get()
	fmt.Println(result, err)

	//  results are instantaneous
	result, err = f.Get()
	fmt.Println(result, err)

	f2 := future.New(func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	}).Then(func(i interface{}) (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(i.(int))
	})

	// times out during first step
	result, timeout, err := f2.GetUntil(2 * time.Second)
	fmt.Println(result, timeout, err)

	f3 := future.New(func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	}).Then(func(i interface{}) (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(i.(int))
	})

	// times out during second step
	result, timeout, err = f3.GetUntil(7 * time.Second)
	fmt.Println(result, timeout, err)

	f4 := future.New(func() (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(inVal)
	}).Then(func(i interface{}) (interface{}, error) {
		return ThingThatTakesALongTimeToCalculate(i.(int))
	})

	// completes both steps
	result, timeout, err = f4.GetUntil(20 * time.Second)
	fmt.Println(result, timeout, err)

}
Output:
800 <nil>
800 <nil>
<nil> true <nil>
<nil> true <nil>
800 false <nil>

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Interface

type Interface interface {
	// Cancel prevents a future that hasn't completed from returning a
	// value. Any current or future calls to Get or GetUntil will return
	// immediately.
	//
	// If the future has already completed or has already been
	// cancelled, calling Cancel will do nothing.
	// After a successful cancel, IsCancelled returns true.
	//
	// Calling Cancel on a future that has not completed does not stop the
	// currently running function. However, any chained functions will not
	// be run and the values returned by the current function are not accessible.
	Cancel()

	// IsCancelled indicates if a future terminated due to cancellation.
	// If Cancel was called and the future's work was not completed, IsCancelled
	// returns true. Otherwise, it returns false
	IsCancelled() bool

	// Get returns the values calculated by the future. It will pause until
	// the future is cancelled or until the value is calculated.
	// If Get is invoked multiple times, the same value will be returned each time.
	// Subsequent calls to Get will return instantaneously.
	//
	// When the future is cancelled, nil is returned for both the value and the error.
	Get() (interface{}, error)

	// GetUntil waits for up to Duration d for the future to complete. If the
	// future completes before the Duration completes, the value and error are returned
	// and timeout is returned as false. If the Duration completes before the future
	// returns, nil is returned for the value and the error and timeout is returned
	// as true.
	//
	// When the future is cancelled, nil is returned for both the value and the error.
	GetUntil(d time.Duration) (interface{}, bool, error)

	// Then allows multiple function calls to be chained together into a single future.
	// Each call is run in order, with the output of the previous call passed into
	// the next function in the chain. If an error occurs at any step in the chain,
	// processing ceases and the error is returned via Get or GetUntil.
	//
	// If Cancel is called before the chain completes, the currently running function
	// will complete silently in the background and all unexecuted functions will
	// not run.
	Then(func(interface{}) (interface{}, error)) Interface
}

Interface represents a future. No concrete implementation is exposed; all access to a future is via this interface.

func New

func New(inFunc func() (interface{}, error)) Interface

New creates a new Future that wraps the provided function.

func NewWithContext

func NewWithContext(ctx context.Context, inFunc func() (interface{}, error)) Interface

NewWithContext creates a new Future that wraps the provided function and cancels when the Done channel of the provided context is closed.

Jump to

Keyboard shortcuts

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