> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/creator/content-creator-zh/chang-jing-sdk7/bian-cheng-mo-shi/async-functions.md).

# 异步函数

了解何时以及如何在场景代码中运行异步函数。

## 概览

你的场景中的大部分代码都是使用单线程同步运行的。这意味着命令会逐行顺序执行。每个命令都必须先等待前一个命令执行完成，然后才能开始。

即使是场景系统中的函数，也会一个接一个地执行，遵循一个 [优先级顺序](/creator/content-creator-zh/chang-jing-sdk7/jia-gou/systems.md#system-execution-order).

同步运行代码可以确保一致性，因为你始终可以确定代码中的命令执行顺序。

另一方面，你的场景需要每秒更新很多次，以构建下一帧。如果代码中的某个部分响应时间太长，那么整个主线程就会被卡住，从而导致帧率下降。

这就是为什么在某些情况下，你会希望某些命令异步运行。这意味着你可以先启动一个任务，同时场景可以继续执行后面的代码行。场景仍然在单线程上运行，但当异步任务等待响应时，其余代码不会被阻塞。

这对于依赖外部服务、而这些服务可能需要一些时间才能响应的任务尤其有用，因为你不希望等待响应的这段空闲时间去阻塞其他任务。

例如：

* 当从 REST API 检索数据时
* 当在区块链上执行交易时

{% hint style="warning" %}
**📔 注意**：请记住，在任务完成执行之前，你的场景可能已经渲染了好几帧。确保你的场景代码足够灵活，能够在异步任务完成期间处理这些中间状态。
{% endhint %}

## 运行一个异步函数

将任意函数标记为 `异步` ，使其异步运行，在等待期间不会阻塞场景的其余代码。

```ts
// 声明异步函数
async function myAsyncTask() {
	// 执行函数步骤
}

// 调用异步函数
myAsyncTask()

// 其余代码继续执行
```

## executeTask 函数

该 `executeTask()` 该函数会异步执行一个 lambda 函数。 `executeTask()` 允许我们在同一个语句中声明并执行该函数。

```ts
executeTask(async () => {
	let data = await myAsyncTask()
	console.log(data)
})

// 其余代码继续执行
```

## then 函数

该 `then` 函数将一个 lambda 函数作为参数，并且只会在前一条语句完成后执行。这个 lambda 函数可以可选地带有输入，这些输入会映射自前一条语句的返回值。

```ts
myAsyncTask().then((data) => {
	console.log(data)
})
```

{% hint style="warning" %}
**📔 注意**：通常最好使用 `executeTask` 方式，而不是使用 `then` 函数。如果你过度依赖 `then` 函数在多个嵌套层级中，你最终可能会陷入所谓的“回调地狱”，代码会变得非常难以阅读和维护。
{% endhint %}

## PointerEvent 和 RayCast 函数

当你的场景使用一个 `PointerEvent` 或 `RayCast` 组件时，碰撞计算会在引擎中异步进行。随后引擎会向场景返回一个结果事件，它可能会比事件被触发时晚一个或几个游戏循环 tick 才到达。

然后你需要创建一个系统，在结果到达的那一帧处理这些结果。

{% hint style="warning" %}
**📔 注意**：如果你通过 [**注册回调**](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/register-callback.md) 的方式处理点击，那么你不需要显式创建一个系统来处理它，但底层仍然会发生同样的事情。
{% endhint %}

参见 [点击事件](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/click-events.md) 和 [射线投射](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/raycasting.md).

{% hint style="info" %}
**💡 提示**：如果射线投射结果的处理需要大量计算（例如运行寻路算法），你可能希望在异步函数中执行该计算。
{% endhint %}

## await 语句

一个 `await` 语句会强制执行在进入下一行代码之前等待响应。 `await` 语句只能在 async 代码块中使用。

```ts
// 声明函数
async function myAsyncTask() {
	try {
		let response = await fetch(callUrl)
		let json = await response.json()
		console.log(json)
	} catch {
		console.log('无法访问该 URL')
	}
}

// 调用函数
myAsyncTask()

// 其余代码继续执行
```

上面的示例执行了一个包含 `fetch()` 从外部 API 检索数据的操作。该 `fetch()` 操作是异步的，因为我们无法预测服务器需要多长时间才能响应。然而，下一行需要先获得该操作的输出，才能将其解析为 JSON。这里的 `await` 语句可确保下一行只有在该操作返回一个值后才会运行。同样地， `response.json()` 函数也是异步的，但下一行需要先解析 JSON 才能将其记录下来。第二个 `await` 语句会强制下一行仅在 JSON 解析完成后才被调用，无论这需要多长时间。

## 为函数调用设置超时

使用 `setTimeout` 在运行某些代码行之前等待一段时间。这需要两个参数：

* 要执行的函数
* 在执行该函数之前需要等待的毫秒数

下面的示例会等待 1000 毫秒（相当于 1 秒），然后执行一个向控制台输出消息的简单函数。

```ts
import { timers } from '@dcl/sdk/ecs'

console.log('这会立即打印出来')

timers.setTimeout(() => {
	// 延迟后运行的函数
    console.log('这会在 1 秒后打印出来')
}, 1000)
```

该 `clearTimeout` 可用于取消一个 `setTimeout` 仍在等待执行的函数的执行。 `setTimeout` 会返回一个计时器 id（一个数字），然后你可以将它传递给 `clearTimeout`。在这种情况下，变量 `timeoutId` 是在执行 `setTimeout`时获取的，然后传递给 `clearTimeout` 以取消它。

```ts
import { timers } from '@dcl/sdk/ecs'

const timeoutId = timers.setTimeout(() => {
    console.log('等待 1 秒后执行此函数')
}, 1000)

timers.clearTimeout(timeoutId)
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.decentraland.org/creator/content-creator-zh/chang-jing-sdk7/bian-cheng-mo-shi/async-functions.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
