> ## Documentation Index
> Fetch the complete documentation index at: https://docs.near.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Yield and Resume

> Wait for an external response and resume execution

export const Github = ({url, start, end, fname, withSourceLink = true}) => {
  const [code, setCode] = useState(null);
  function toRaw(ref) {
    const fullUrl = ref.slice(ref.indexOf('https'));
    const [url] = fullUrl.split('#');
    const [org, repo, , branch, ...pathSeg] = new URL(url).pathname.split('/').slice(1);
    return `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${pathSeg.join('/')}`;
  }
  async function fetchCode(url, fromLine, toLine) {
    let res;
    if (typeof window !== 'undefined') {
      const validUntil = localStorage.getItem(`${url}-until`);
      if (validUntil && Number(validUntil) > Date.now()) {
        res = localStorage.getItem(url);
      }
    }
    if (!res) {
      try {
        res = await (await fetch(url)).text();
        if (typeof window !== 'undefined') {
          localStorage.setItem(url, res);
          localStorage.setItem(`${url}-until`, String(Date.now() + 60000));
        }
      } catch {
        return 'Error fetching code, please try reloading';
      }
    }
    let body = res.split('\n');
    const from = fromLine ? Number(fromLine) - 1 : 0;
    const to = toLine ? Number(toLine) : body.length;
    body = body.slice(from, to);
    const precedingSpace = body.reduce((prev, line) => {
      if (line.length === 0) return prev;
      const spaces = line.match(/^\s+/);
      if (spaces) return Math.min(prev, spaces[0].length);
      return 0;
    }, Infinity);
    return body.map(line => line.slice(precedingSpace === Infinity ? 0 : precedingSpace)).join('\n');
  }
  function buildSourceUrl(url, start, end) {
    const base = url.split('#')[0];
    if (start && end) return `${base}#L${start}-L${end}`;
    if (start) return `${base}#L${start}`;
    return base;
  }
  useEffect(() => {
    const rawUrl = toRaw(url);
    fetchCode(rawUrl, start, end).then(res => setCode(res));
  }, [url, start, end]);
  const sourceUrl = buildSourceUrl(url, start, end);
  const startLine = start ? Number(start) : 1;
  const fileName = fname ?? sourceUrl.split('/').pop();
  return <div className="rounded-[0.625rem] border border-[#d0d7de] dark:border-[#30363d] overflow-hidden my-5 text-[0.8125rem] font-mono shadow-sm dark:shadow-[0_4px_24px_rgba(0,0,0,0.18)]">

      {}
      <div className="flex items-center justify-between py-2 px-[0.875rem] bg-[#f6f8fa] dark:bg-[#161b22] border-b border-[#d0d7de] dark:border-[#30363d]">
        <div className="flex items-center gap-2 text-[#656d76] dark:text-[#8b949e]">
          <svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor">
            <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z" />
          </svg>
          <span className="text-xs font-medium text-[#1f2328] dark:text-[#e6edf3]">
            {fileName}
          </span>
        </div>
        {start && end && <span className="text-[0.6875rem] text-[#656d76] dark:text-[#8b949e] bg-[#eaeef2] dark:bg-[#21262d] border border-[#d0d7de] dark:border-[#30363d] rounded-full py-0.5 px-2">
            Lines {start}–{end}
          </span>}
      </div>

      {}
      <div className="overflow-auto max-h-[480px] bg-white dark:bg-[#0d1117] [&_tr]:border-b-0 [&_td]:border-b-0">
        {code === null ? <div className="py-5 px-4 text-xs text-[#656d76] dark:text-[#6e7681]">
            Loading...
          </div> : <table className="w-full border-collapse leading-[1.6]">
            <tbody>
              {code.split('\n').map((line, i) => <tr key={i} className="align-top border-0">
                  <td style={{
    minWidth: '60px'
  }} className="select-none pl-2 pr-3 text-right text-[0.7rem] text-[#8c959f] dark:text-[#3d444d] w-[1%] whitespace-nowrap border-r border-0 border-r-[#d0d7de] dark:border-r-[#21262d]">
                    {startLine + i}
                  </td>
                  <td className="pl-4 pr-6 text-[0.8125rem] text-[#1f2328] dark:text-[#e6edf3] whitespace-pre">
                    {line || ' '}
                  </td>
                </tr>)}
            </tbody>
          </table>}
      </div>

      {}
      {withSourceLink && <div className="flex justify-end py-1.5 px-[0.875rem] bg-[#f6f8fa] dark:bg-[#161b22] border-t border-[#d0d7de] dark:border-[#21262d]">
          <a href={sourceUrl} target="_blank" rel="noreferrer noopener" className="text-[0.6875rem] font-medium text-[#656d76] dark:text-[#8b949e] no-underline flex items-center gap-[0.3rem] hover:text-[#1f2328] dark:hover:text-[#e6edf3] transition-colors">
            View on GitHub
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
              <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
              <polyline points="15 3 21 3 21 9" />
              <line x1="10" y1="14" x2="21" y2="3" />
            </svg>
          </a>
        </div>}
    </div>;
};

NEAR smart contracts can **yield** execution, until an **external** service **resumes** them. In practice, the contract yields a **cross-contract call** to itself, until an external service executes a function and the contract decides to resume.

This is a powerful feature that allows contracts to wait for external events, such as a response from an oracle, before continuing execution.

<Info>
  The contract can wait for 200 blocks - around 2 minutes - after which the yielded function will execute, receiving a "timeout error" as input
</Info>

***

## Yielding a Promise

Let's look at an example that takes a prompt from a user (e.g. "What is 2+2"), and yields the execution until an external service provides a response.

<Tabs>
  <Tab title="🦀 Rust">
    <Github fname="lib.rs" language="rust" url="https://github.com/near-examples/yield-resume/blob/main/contract/src/lib.rs" start="43" end="70" />
  </Tab>

  <Tab title="🐹 GO">
    <Github fname="main.go" language="go" url="https://github.com/Emir-Asanov/near-go-examples/blob/example-release-1/yield-resume/main.go" start="23" end="58" />
  </Tab>

  <Tab title="🐍 Python">
    ```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
    from near_sdk_py import call, view, near, Context
    from near_sdk_py.collections import UnorderedMap
    import json

    class YieldResumeContract:
        def __init__(self):
            # Store pending requests
            self.requests = UnorderedMap("r")
            self.request_id = 0
        
        @call
        def ask_assistant(self, prompt):
            """
            Creates a yielded promise that will be resumed when an external service responds
            
            Args:
                prompt: The question to ask the assistant
            """
            # Create a new request ID
            request_id = self.request_id
            self.request_id += 1
            
            # Create arguments for the callback function
            callback_args = json.dumps({"request_id": request_id})
            
            # Create a yielded promise to call return_external_response on this contract
            yield_id = near.promise_create(
                Context.current_account_id(),
                "return_external_response",
                callback_args,
                0,  # No deposit
                30000000000000  # Gas
            )
            
            # Store the yield ID and prompt for the external service to find
            self.requests[str(request_id)] = {
                "yield_id": yield_id,
                "prompt": prompt
            }
            
            # Return the yield_id and request_id for tracking
            return {
                "yield_id": yield_id,
                "request_id": request_id
            }
    ```
  </Tab>
</Tabs>

#### Creating a Yielded Promise

In the example above, we are creating a [`Promise`](./crosscontract#promises) to call the contract's function `return_external_response`.

Notice that we create the `Promise` using `env::promise_yield_create` in Rust or `near.promise_create` in Python (the Python SDK uses standard promises for yielding), which will create an **identifier** for the yielded promise in the `YIELD_REGISTER`.

#### Retrieving the Yielded Promise ID

We read the `YIELD_REGISTER` to retrieve the `ID` of our yielded promise. We store the `yield_id` and the user's `prompt` so the external service query them (the contract exposes has a function to list all requests).

#### Returning the Promise

Finally, we return the `Promise`, which will **not execute immediately**, but will be **yielded** until the external service provides a response.

<Accordion title="What is that `self.request_id` in the code?">
  The `self.request_id` is an internal unique identifier that we use to keep track of stored requests. This way, we can delete the request once the external service provides a response (or the waiting times out)

  Since we only use it to simplify the process of keeping track of the requests, you can remove it if you have a different way of tracking requests (e.g. an indexer)
</Accordion>

***

## Signaling the Resume

The `env::promise_yield_resume` function in Rust or `near.promise_yield_resume` in Python allows us to signal which yielded promise should execute, as well as which parameters to pass to the resumed function.

<Tabs>
  <Tab title="🦀 Rust">
    <Github fname="lib.rs" language="rust" url="https://github.com/near-examples/yield-resume/blob/main/contract/src/lib.rs" start="72" end="75" />
  </Tab>

  <Tab title="🐹 GO">
    <Github fname="main.go" language="go" url="https://github.com/Emir-Asanov/near-go-examples/blob/example-release-1/yield-resume/main.go" start="60" end="80" />
  </Tab>

  <Tab title="🐍 Python">
    ```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
    @call
    def respond(self, yield_id, response):
        """Called by the external service to provide a response to a prompt"""
        return near.promise_yield_resume(yield_id, json.dumps({"response": response}))
    ```
  </Tab>
</Tabs>

In the example above, the `respond` function would be called by an external service, passing which promise should be resume (`yield_id`), and the response to the prompt.

<Warning>
  **Gatekeeping the Resume**

  Since the function used to signal the resume is public, developers must make sure to guard it properly to avoid unwanted calls. This can be done by simply checking the caller of the function
</Warning>

***

## The Function that Resumes

The function being resumed will have access to all parameters passed to it, including those passed during the yield creation, or the external service response.

<Tabs>
  <Tab title="🦀 Rust">
    <Github fname="lib.rs" language="rust" url="https://github.com/near-examples/yield-resume/blob/main/contract/src/lib.rs" start="77" end="89" />
  </Tab>

  <Tab title="🐹 GO">
    <Github fname="main.go" language="go" url="https://github.com/Emir-Asanov/near-go-examples/blob/example-release-1/yield-resume/main.go" start="82" end="119" />
  </Tab>

  <Tab title="🐍 Python">
    ```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
    @call
    def return_external_response(self, request_id, response=None):
        """
        Function that gets called when the yielded promise resumes
        
        Args:
            request_id: The ID of the request (passed when promise was created)
            response: The response from the external service (or None if timed out)
        """
        # Remove the request from our tracking
        request_data = self.requests.get(str(request_id))
        if request_data:
            del self.requests[str(request_id)]
        
        # Handle timeout case
        if response is None:
            return {"error": "Timeout waiting for external service", "request_id": request_id}
        
        # Return the response from the external service
        return {
            "request_id": request_id,
            "response": response,
            "success": True
        }
    ```
  </Tab>
</Tabs>

In the example above, the `return_external_response` receives parameters:

1. A `request_id` - passed on [creation](#creating-a-yielded-promise) - which is used to remove the request from the state
2. A `response` - passed when [signaling to resume](#signaling-the-resume) - which contains the external response, or `None` if the contract timed out while waiting

<Tip>
  **There's plenty of time**

  The contract will be able to wait for 200 blocks - around 2 minutes - before timing out
</Tip>

<Info>
  Notice that, in this particular example, we choose to return a value both if there is a response or a time out

  The reason to not raise an error, is because we are changing the state (removing the request in line `#7`), and raising an error would revert this state change
</Info>

***

## Managing State

When using yield and resume, it's important that you carefully manage the contract's state. Because of its asynchronous execution, the contract function in which the contract yields and resumes are independent.

If you change the state of the contract in the function where you yield the promise (the `request` function here), then you need to make sure that you revert the state in the function that resumes (the `return_external_response` function here) in the case that the promise times out or the response is invalid.

It is best practice to check the validity of the response within the function where the resume is signaled (the `respond` function here) and panic if the response is not valid; the external service can attempt to respond again before the promise times out. You should not panic in `return_external_response` as this is only called when the promise has been resolved (it was resumed or timed out), meaning it can't be resumed again, and the state in `request` has been settled. You should gracefully complete the function and revert the state.

<Info>
  Check more docs on [callback security](../security/callbacks) and [reentrancy attacks](../security/reentrancy) to avoid common pitfalls when dealing with asynchronous calls.
</Info>

***

## Complete Example

Here's a more complete implementation of a yield-resume pattern in Python:

<Tabs>
  <Tab title="🐍 Python">
    ```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
    from near_sdk_py import view, call, near, Context
    from near_sdk_py.collections import UnorderedMap
    import json

    class AIAssistantContract:
        def __init__(self):
            # Track all pending requests
            self.requests = UnorderedMap("r")
            self.request_counter = 0
        
        @call
        def ask_question(self, question):
            """
            Ask a question to the AI assistant
            
            The execution will yield until an external AI service responds
            """
            # Create a unique ID for this request
            request_id = self.request_counter
            self.request_counter += 1
            
            # Create callback args - will be passed to our callback function
            callback_args = json.dumps({
                "request_id": request_id
            })
            
            # Create the promise - this will yield until resumed
            promise_id = near.promise_create(
                Context.current_account_id(),  # Call this contract
                "process_ai_response",         # Call this method when resumed
                callback_args,                 # Pass these arguments
                0,                             # No attached deposit
                30000000000000                 # Gas for execution (30 TGas)
            )
            
            # Store the request for the external service to find
            self.requests[str(request_id)] = {
                "prompt": question,
                "promise_id": promise_id,
                "user": Context.predecessor_account_id(),
                "timestamp": Context.block_timestamp()
            }
            
            return {
                "request_id": request_id,
                "status": "processing"
            }
        
        @view
        def get_pending_requests(self):
            """Returns all pending requests for the AI service to process"""
            return [
                {
                    "request_id": int(req_id), 
                    "data": self.requests[req_id]
                } 
                for req_id in self.requests.keys()
            ]
        
        @call
        def provide_ai_response(self, request_id, response):
            """
            Called by the AI service to provide a response
            
            Args:
                request_id: ID of the request being answered
                response: The AI's response to the question
            """
            request_id_str = str(request_id)
            
            # Verify the request exists
            if request_id_str not in self.requests:
                raise Exception(f"No pending request with ID {request_id}")
            
            # Get the request data
            request = self.requests[request_id_str]
            
            # Resume the promise with the AI's response
            result = near.promise_yield_resume(
                request["promise_id"],
                json.dumps({"ai_response": response})
            )
            
            return {"success": result}
        
        @call
        def process_ai_response(self, request_id, ai_response=None):
            """
            Called when a yielded promise resumes
            
            This is either called by provide_ai_response or by a timeout
            
            Args:
                request_id: ID of the request
                ai_response: The AI's response or None if timed out
            """
            request_id_str = str(request_id)
            
            # Cleanup - remove from pending requests
            if request_id_str in self.requests:
                request = self.requests[request_id_str]
                del self.requests[request_id_str]
            else:
                request = None
            
            # Handle timeout case
            if ai_response is None:
                return {
                    "request_id": request_id,
                    "status": "timeout",
                    "message": "The AI service did not respond in time"
                }
            
            # Return the AI's response
            return {
                "request_id": request_id,
                "status": "complete",
                "question": request["prompt"] if request else "Unknown",
                "answer": ai_response,
                "user": request["user"] if request else "Unknown"
            }
    ```
  </Tab>
</Tabs>

This example demonstrates a complete yield-resume pattern for an AI assistant contract where:

1. A user asks a question through `ask_question`
2. The contract creates a yielded promise and stores the question
3. An external AI service periodically checks for new questions using `get_pending_requests`
4. When the AI has an answer, it calls `provide_ai_response` to resume the yielded promise
5. The `process_ai_response` function executes with the AI's answer (or timeout) and returns the result
