|
| 1 | +# Context and Timeout |
| 2 | + |
| 3 | +In Golang, we usually use [context](https://golang.org/pkg/context/) to cancel IO blocking tasks. |
| 4 | +Because Rod uses WebSocket to talk to the browser, literally all control signals Rod send to the browser |
| 5 | +are an IO blocking. The use of context is not special for Rod, it follows the standard way. |
| 6 | + |
| 7 | +## Cancellation |
| 8 | + |
| 9 | +For example, we have a sample code like below, it simply creates a blank page and navigates it to the "github.com": |
| 10 | + |
| 11 | +```go |
| 12 | +page := rod.New().MustConnect().MustPage("") |
| 13 | +page.MustNavigate("http://github.com") |
| 14 | +``` |
| 15 | + |
| 16 | +Now, suppose we want to cancel the `MustNavigate` if it takes more than 2 seconds. |
| 17 | +In Rod we can do something like this to achieve it: |
| 18 | + |
| 19 | +```go |
| 20 | +page := rod.New().MustConnect().MustPage("") |
| 21 | + |
| 22 | +ctx, cancel := context.WithCancel(context.Background()) |
| 23 | +pageWithCancel := page.Context(ctx) |
| 24 | + |
| 25 | +go func() { |
| 26 | + time.Sleep(2 * time.Second) |
| 27 | + cancel() |
| 28 | +}() |
| 29 | + |
| 30 | +pageWithCancel.MustNavigate("http://github.com") |
| 31 | +``` |
| 32 | + |
| 33 | +We use the `page.Context` to create a shallow copy of the `page`. Whenever we call the `cancel`, the operations |
| 34 | +on the `pageWithCancel` will be canceled, it can be any operation, not just `MustNavigate`. |
| 35 | + |
| 36 | +It's not special for Rod, you can find similar APIs like [this one](https://golang.org/pkg/net/http/#Request.WithContext) in the standard library. |
| 37 | + |
| 38 | +Because `pageWithCancel` is not `page`, operations on `page` will not be affected by the cancellation: |
| 39 | + |
| 40 | +```go |
| 41 | +... |
| 42 | + |
| 43 | +pageWithCancel.MustNavigate("http://github.com") // will be canceled after 2 seconds |
| 44 | +page.MustNavigate("http://github.com") // won't be canceled after 2 seconds |
| 45 | +``` |
| 46 | + |
| 47 | +## Timeout |
| 48 | + |
| 49 | +The code above is just a way to timeout an operation. In Golang, timeout is usually just a special case of cancellation. |
| 50 | +Because it's so useful, we created a helper to do the same thing above, it's called `Timeout`, so the code above can be reduced like below: |
| 51 | + |
| 52 | +```go |
| 53 | +page := rod.New().MustConnect().MustPage("") |
| 54 | +page.Timeout(2 * time.Second).MustNavigate("http://github.com") |
| 55 | +``` |
| 56 | + |
| 57 | +The `page.Timeout(2 * time.Second)` is the previous `pageWithCancel`. |
| 58 | +Not just `Page`, `Browser` and `Element` also support the same context APIs. |
| 59 | + |
| 60 | +## Detect timeout |
| 61 | + |
| 62 | +How do I know if an operation is timed out or not? In Golang, timeout is usually a type of error. It's not special for Rod. |
| 63 | +For the code above we can do this to detect timeout: |
| 64 | + |
| 65 | +```go |
| 66 | +page := rod.New().MustConnect().MustPage("") |
| 67 | + |
| 68 | +err := rod.Try(func() { |
| 69 | + page.Timeout(2 * time.Second).MustNavigate("http://github.com") |
| 70 | +}) |
| 71 | +if errors.Is(err, context.DeadlineExceeded) { |
| 72 | + // code for timeout error |
| 73 | +} else if err != nil { |
| 74 | + // code for other types of error |
| 75 | +} |
| 76 | +``` |
| 77 | + |
| 78 | +Here we use `rod.Try` to wrap the function that may throw a timeout error. |
| 79 | + |
| 80 | +We will talk more about error handing at [Error Handling](error-handling.md). |
| 81 | + |
| 82 | +[Next Chapter](error-handling.md) |
0 commit comments