In Golang, we usually use Context to abort long-running tasks. Rod uses Context to handle cancellations for IO blocking operations, most times it's timeout. You need to pay special attention to them.
If you are not familiar with Context, please read Understand Context first.
For example, the code below creates a blank page and navigates it to the "github.com":
page := rod.New().MustConnect().MustPage()
page.MustNavigate("http://github.com")
Now, suppose we want to cancel the MustNavigate
if it takes more than 2 seconds.
In Rod we can do something like this:
page := rod.New().MustConnect().MustPage()
ctx, cancel := context.WithCancel(context.Background())
pageWithCancel := page.Context(ctx)
go func() {
time.Sleep(2 * time.Second)
cancel()
}()
// The 2 lines below share the same context, they will be canceled after 2 seconds in total
pageWithCancel.MustNavigate("http://github.com")
pageWithCancel.MustElement("body")
We use the page.Context
to create a shallow clone of the page
. Whenever we call the cancel
, all the sub operations
triggered by the pageWithCancel
will be canceled, it can be any operation, not just MustNavigate
.
The origin page
won't be affected, if we use it to call operations they won't be cancelled.
This style is not special for Rod, you can find similar APIs like Request.WithContext in the standard library.
Because pageWithCancel
and page
are independent to each other, operations triggered by page
will not be affected by the cancellation:
page.MustNavigate("http://github.com") // won't be canceled after 2 seconds
The code above is just a way to timeout an operation. In Golang, timeout is usually just a special case of cancellation.
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:
page := rod.New().MustConnect().MustPage()
page.Timeout(2 * time.Second).MustNavigate("http://github.com")
The page.Timeout(2 * time.Second)
is the previous pageWithCancel
.
Not just Page
, Browser
and Element
also have the same context helpers.
If you want to keep using the same instance after some operation, you can use the Page.CancelTimeout
helper to cancel the timeout:
page.
Timeout(2 * time.Second).MustElement("a").
CancelTimeout().
MustElement("b") // This line won't be affected by the 2 seconds timeout.
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. For the code above we can do this to detect timeout:
page := rod.New().MustConnect().MustPage()
err := rod.Try(func() {
page.Timeout(2 * time.Second).MustNavigate("http://github.com")
})
if errors.Is(err, context.DeadlineExceeded) {
fmt.Println("timeout error")
} else if err != nil {
fmt.Println("other types of error")
}
Here we use rod.Try
to wrap the function that may throw a timeout error.
We will talk more about error handing at Error Handling.