Principle:MarketSquare Robotframework browser Async Keyword Execution
| Property | Value |
|---|---|
| Principle Name | Async Keyword Execution |
| Domains | Test_Automation, Async_Programming |
| Workflow | Browser_Test_Authoring |
| Repository | MarketSquare/robotframework-browser |
| Type | Principle |
Overview
Running keywords asynchronously using a promise-based pattern handles race conditions in browser testing where a waiting operation must begin before the triggering action occurs.
Description
The Async Keyword Execution principle addresses a fundamental timing challenge in browser automation: certain operations need to be "listening" before the action that triggers them occurs. For example:
- You want to capture a network response triggered by clicking a button, but the response may arrive before a synchronous "wait for response" keyword could start listening.
- You want to wait for a file download that is initiated by a click, but the download event fires immediately upon clicking.
- You want to wait for a navigation that is triggered by form submission.
The solution is a promise-based pattern that separates an operation into three phases:
- Promise To (start): Begin the waiting operation in a background thread. This returns a
Futureobject (promise) immediately while the operation continues running concurrently. - Trigger: Perform the action that will trigger the event being waited for (e.g., click a button, submit a form).
- Wait For (resolve): Wait for the promise to complete and retrieve its result. If the background operation encountered an error, the error is raised at this point.
This pattern uses Python's concurrent.futures.Future under the hood. The Promise To keyword submits the specified Browser keyword to a thread pool executor, and the Wait For keyword calls .result() on the future.
Important constraints:
- Only Browser library keywords can be used with
Promise To. User keywords or keywords from other libraries are not supported. - The library tracks all unresolved promises and provides
Wait For All Promisesto wait for any that have not been explicitly awaited. - Arguments to the promised keyword are passed as strings and resolved using the same argument parsing and type conversion that Robot Framework uses.
Additionally, the Wait For Elements State keyword provides a synchronous waiting mechanism for element-level state changes (visible, hidden, attached, detached, enabled, disabled, focused, etc.) without requiring the async pattern. It is used when you simply need to wait for an element to reach a specific state before proceeding.
Usage
Use this principle when:
- You need to capture a network response or request that is triggered by a user action.
- You need to wait for a file download initiated by a click.
- You need to handle any event where the listener must be active before the trigger.
- You need to wait for navigation events triggered by form submissions or link clicks.
Use Wait For Elements State when you simply need to wait for an element to become visible, hidden, enabled, or reach another state.
Theoretical Basis
PROMISE_TO(keyword_name, *args):
# Validate that keyword_name is a Browser library keyword
known_keyword = LOOKUP_KEYWORD(normalize(keyword_name))
IF known_keyword NOT FOUND:
RAISE ValueError("Unknown keyword")
# Parse and convert arguments
positional, named = RESOLVE_ARGUMENTS(known_keyword, args)
# Submit to thread pool executor
future = EXECUTOR.submit(known_keyword, *positional, **named)
# Track as unresolved
unresolved_promises.add(future)
# Wait until the future is actually running (not just queued)
WHILE NOT (future.running() OR future.done()):
SLEEP(10ms)
RETURN future
WAIT_FOR(*promises):
# Remove from unresolved tracking
unresolved_promises -= set(promises)
# Get results (blocks until complete)
IF len(promises) == 1:
RETURN promises[0].result() # Raises exception if failed
ELSE:
RETURN [p.result() FOR p IN promises]
WAIT_FOR_ALL_PROMISES():
WAIT_FOR(*unresolved_promises)
# The typical usage pattern:
ASYNC_PATTERN_EXAMPLE():
promise = PROMISE_TO("Wait For Response", "matcher=", "timeout=3")
CLICK("#submit-button") # Trigger action
response = WAIT_FOR(promise) # Collect result
# Synchronous element state waiting (separate mechanism):
WAIT_FOR_ELEMENTS_STATE(selector, state, timeout):
end_time = NOW() + timeout
LOOP:
TRY:
CHECK element matches state (visible, hidden, attached, etc.)
RETURN # Success
CATCH error:
IF NOW() > end_time:
RAISE timeout error
RETRY