Skip to content

Latest commit

 

History

History
93 lines (66 loc) · 3.21 KB

context-and-timeout.md

File metadata and controls

93 lines (66 loc) · 3.21 KB

Context and Timeout

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.

Cancellation

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, the all 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

Timeout

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.

Cancel timeout

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.

Detect 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.