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

# Modelo de programación asincrónica

> Modelo de programación asincrónica

Microsoft Game Development Kit (GDK) implementa un nuevo patrón para las API asincrónicas que
aborda los comentarios que hemos recibido de los desarrolladores de juegos con respecto
al patrón asincrónico implementado como parte del modelo de programación de XBOX One ERA.
Nuestro objetivo es que este nuevo patrón sea mucho más fácil de integrar en
las arquitecturas de juego típicas y proporcione a los desarrolladores de juegos el alto grado de
control que han solicitado. En este tema se describe ese patrón de
diseño y se ofrece una propuesta de biblioteca que puede usarse para
implementar patrones asincrónicos.

<a id="gdk_asynchronous_model" />

## Modelo conceptual

La programación asincrónica en Microsoft Game Development Kit (GDK) se divide en 2 componentes principales:
tareas y colas de tareas. Aunque hay más funcionalidad en las
bibliotecas, todo el modelo conceptual utiliza estos 2 componentes principales.

Una tarea es un único conjunto de trabajo asincrónico que se puede iniciar, cuyo
estado se puede comprobar, que potencialmente se puede cancelar, que se completa y que devuelve su
información de finalización. Para el modelo de Microsoft Game Development Kit (GDK), las tareas se componen de dos
cuerpos: la devolución de llamada de trabajo y la devolución de llamada de finalización. Esto permite
más control, como el procesamiento paralelo completo o el trabajo en paralelo combinado
con una finalización de un solo subproceso.

Una cola de tareas es un contenedor que pone en cola tanto las devoluciones de llamada de trabajo como las de
finalización para su ejecución posterior. Hay dos colas internas en una cola de
tareas, denominadas puertos, que controlan las devoluciones de llamada de trabajo y de finalización
por separado. Se denominan puerto de trabajo y puerto de finalización.

**Figura 1.  Diagrama de una tarea y una cola de tareas**

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/async_task_taskqueue_diagram.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=89181466bdb096dbacd570ff0f1dcac5" alt="Diagrama de una tarea y una cola de tareas." width="660" height="314" data-path="images/gdk/features/common/async_task_taskqueue_diagram.png" />

Cada puerto de la cola de tareas se configura de forma diferente [en el momento de la
creación](/reference/system/xtaskqueue/functions/xtaskqueuecreate) para crear un comportamiento de ejecución de devoluciones de llamada distinto. Por
ejemplo, el puerto de trabajo podría configurarse para ser asincrónico y el
puerto de finalización podría configurarse para ejecutarse en serie en un subproceso principal. Se
puede establecer una configuración manual para habilitar el control completo sobre el comportamiento de
ejecución. Los modos de configuración de puertos se [explican más adelante](#controlling_work_dispatching).

Cuando se inicia una tarea asincrónica, las devoluciones de llamada no se ponen en cola en la
cola de tareas inmediatamente. Un [proveedor asincrónico](/build/core-features/common/async/async-libraries/async-library-xasyncprovider)
controla los cambios de estado para garantizar que el trabajo se ponga en cola y se distribuya
antes de que la devolución de llamada de finalización se ponga en cola y se distribuya.

La cola de tareas no controla directamente los subprocesos. En su lugar,
depende de una llamada externa para [distribuir](/reference/system/xtaskqueue/functions/xtaskqueuedispatch) sus puertos. Las
llamadas externas determinan el comportamiento de los subprocesos y la simultaneidad. La
propia cola de tareas es completamente segura para subprocesos.

**Figura 2.  Puerto distribuido en varios subprocesos**

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/async_port_dispatch_multithread.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=697aae48e97cb3aa23845108060a1882" alt="Imagen que muestra un puerto distribuido en varios subprocesos." width="628" height="230" data-path="images/gdk/features/common/async_port_dispatch_multithread.png" />

¡Eso es esencialmente todo! Las devoluciones de llamada de una tarea se ponen en cola en los
puertos de trabajo y de finalización de una cola de tareas, y esa cola de tareas hace que esas
devoluciones de llamada se distribuyan de alguna manera. La API contiene un conjunto completo de
funcionalidad para administrar colas de tareas, comprobar el estado de las devoluciones de llamada, hacer seguimiento de
los datos de trabajo, crear un control de tareas personalizado y mucho más.

Las llamadas API asincrónicas de Microsoft Game Development Kit (GDK) siempre implementan la devolución de llamada de trabajo internamente
y las devoluciones de llamada de finalización son siempre opcionales. Para usos más allá de las llamadas asincrónicas
de Microsoft Game Development Kit (GDK), debe proporcionar la devolución de llamada de trabajo.

## Requisitos

Los desarrolladores de juegos han enumerado los siguientes requisitos para las llamadas API.

1. Preferir llamadas sincrónicas en lugar de llamadas asincrónicas
2. Proporcionar operaciones asincrónicas con sondeo
3. Proporcionar operaciones asincrónicas con devoluciones de llamada
4. Proporcionar control sobre el subproceso en el que se ejecuta el trabajo asincrónico
5. Proporcionar control sobre el subproceso en el que se ejecutan las devoluciones de llamada
   de finalización

## Tipos de API

Microsoft Game Development Kit (GDK) se esfuerza por ser muy directo en el diseño de su
API. Los desarrolladores de juegos son expertos en ajustar su código para
maximizar el uso del hardware. Les damos el control siempre que es
posible. Las implementaciones de API se dividen en los siguientes tipos.

* **Seguras para subprocesos sensibles al tiempo:** una API segura para subprocesos sensibles al tiempo es aquella que se puede
  llamar en un subproceso sensible al tiempo. Tenga en cuenta que, aunque esto normalmente
  significa que la API es trivial o muy rápida, el concepto clave es que
  las características de rendimiento de la API son *coherentes*. Siempre
  son sincrónicas y nunca necesitan tener una versión
  asincrónica. Estas API deben documentarse como seguras para tiempo sensible.

* **No seguras para subprocesos sensibles al tiempo:** no es seguro llamar a estas API desde
  el subproceso de representación. Sus características de rendimiento pueden variar
  considerablemente. La mayoría de las API pertenecen a esta categoría.

* **Asincrónicas:** estas API son de naturaleza asincrónica, como una
  llamada a un servicio web. Usan el patrón asincrónico descrito en
  este tema. Las API asincrónicas no son tan comunes en Microsoft Game Development Kit (GDK) como en el modelo de programación de XBOX One ERA: una API
  asincrónica suele ser de larga duración y cancelable. Excepto en algunos
  casos de uso específicos, las API asincrónicas tendrán una
  versión sincrónica no segura para tiempo crítico. La llamada a una API
  asincrónica siempre debe ser segura para tiempo crítico.

* **Notificaciones:** las notificaciones son de naturaleza periódica y no tienen un
  final definido. Están relacionadas con las API asincrónicas pero, debido a
  su naturaleza periódica, deben tener un aspecto y un comportamiento diferentes para los
  desarrolladores. Registrarse para una notificación siempre debe ser
  seguro para tiempo crítico.

## Patrón de API asincrónica

Microsoft Game Development Kit (GDK) presenta un patrón de API asincrónica de uso general que los componentes de Microsoft Game Development Kit (GDK) pueden usar para proporcionar compatibilidad asincrónica coherente. En el
núcleo hay una estructura similar a OVERLAPPED denominada [XAsyncBlock](/reference/system/xasync/structs/xasyncblock):

```c++ theme={null}
typedef void CALLBACK XAsyncCompletionRoutine(struct XAsyncBlock* asyncBlock);

struct XAsyncBlock
{
    XTaskQueueHandle queue;
    void* context;
    XAsyncCompletionRoutine* callback;
    unsigned char internal[sizeof(void*) * 4];
};
```

Un [XAsyncBlock](/reference/system/xasync/structs/xasyncblock) es una estructura proporcionada por el autor de la llamada. El autor de la llamada rellena
los campos opcionales de esta estructura, como se muestra en la tabla siguiente.

| Campo        | Descripción                                                                                                                                                                                                                                                                             |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **queue**    | Un identificador de cola de tareas que puede controlar qué subprocesos ejecutan la llamada asincrónica. Si este parámetro es null, se usa la cola de tareas del proceso. Si la cola de tareas del proceso se ha establecido en null, la llamada genera un error con E\_NO\_TASK\_QUEUE. |
| **context**  | Un puntero de contexto opcional que se pasa a la función de devolución de llamada.                                                                                                                                                                                                      |
| **callback** | Una función de devolución de llamada opcional a la que se llama cuando se completa la operación.                                                                                                                                                                                        |

El sistema usa los campos **Internal** y no deben modificarse.
Los campos que puede establecer el usuario en esta estructura no deben modificarse
durante una operación asincrónica. Un [XAsyncBlock](/reference/system/xasync/structs/xasyncblock) debe permanecer en memoria durante toda la
duración de la operación asincrónica. Si el [XAsyncBlock](/reference/system/xasync/structs/xasyncblock) se asigna dinámicamente,
la devolución de llamada de finalización es el momento más temprano en el que se puede eliminar.

Además de [XAsyncBlock](/reference/system/xasync/structs/xasyncblock), hay un pequeño número de API auxiliares, que se muestran a continuación.

```c++ theme={null}
STDAPI XAsyncGetStatus(XAsyncBlock* asyncBlock, bool wait);

STDAPI XAsyncGetResultSize(XAsyncBlock* asyncBlock, size_t* bufferSize);

STDAPI_(void) XAsyncCancel(XAsyncBlock* asyncBlock);

typedef HRESULT CALLBACK XAsyncWork(XAsyncBlock* asyncBlock);

STDAPI XAsyncRun(XAsyncBlock* asyncBlock, XAsyncWork* work);
```

[XAsyncGetStatus](/reference/system/xasync/functions/xasyncgetstatus) devuelve el estado de una llamada asincrónica. Cuando
comienza la llamada, este estado es E\_PENDING. Cambia a S\_OK o a un
error específico cuando se completa. Si se cancela la llamada, devuelve
E\_ABORT.

[XAsyncGetResultSize](/reference/system/xasync/functions/xasyncgetresultsize) devuelve el tamaño de búfer necesario para obtener los
resultados de la llamada. La API real para capturar los resultados se adapta a
cada llamada asincrónica.

[XAsyncCancel](/reference/system/xasync/functions/xasynccancel) puede usarse para cancelar una llamada. La cancelación depende de la operación que se cancela y puede producirse de forma sincrónica, asincrónica o no producirse en absoluto. Si se cancela una operación,
[XAsyncGetResult](/reference/system/xasyncprovider/functions/xasyncgetresult), [XAsyncGetResultSize](/reference/system/xasync/functions/xasyncgetresultsize) o [XAsyncGetStatus](/reference/system/xasync/functions/xasyncgetstatus) devuelven E\_ABORT. Una llamada
cancelada señala el parámetro [XAsyncCompletionRoutine](/reference/system/xasync/functions/xasynccompletionroutine) del [XAsyncBlock](/reference/system/xasync/structs/xasyncblock) e invoca su
devolución de llamada.

[XAsyncRun](/reference/system/xasync/functions/xasyncrun) es un método auxiliar que puede ejecutar de forma asincrónica cualquier código.

### Uso de la API asincrónica

En primer lugar, veamos una API sincrónica en el siguiente ejemplo de código.

```c++ theme={null}
HRESULT XGameSaveGetRemainingQuota(XGameSaveProviderHandle provider,
int64_t* remainingQuota);
```

Esta API llama a un servicio web para determinar cuánto almacenamiento de partidas guardadas
queda todavía. Para agregar compatibilidad asincrónica, declaramos un par de nuevas
API.

```c++ theme={null}
HRESULT XGameSaveGetRemainingQuotaAsync(XGameSaveProviderHandle
provider, XAsyncBlock* async);

HRESULT XGameSaveGetRemainingQuotaResult(XAsyncBlock* async,
int64_t* remainingQuota);
```

[XGameSaveGetRemainingQuotaAsync](/reference/system/xgamesave/functions/xgamesavegetremainingquotaasync) devuelve S\_OK si la llamada asincrónica se ha
iniciado (como esta API es solo asincrónica, no tiene sentido devolver
E\_PENDING). [XGameSaveGetRemainingQuotaResult](/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult) devuelve E\_PENDING
hasta que la llamada se completa.

Veamos esto en la práctica de la siguiente manera.

```c++ theme={null}
// providerHandle is a previously obtained XGameSaveProviderHandle.

XAsyncBlock* b = new XAsyncBlock;
ZeroMemory(b, sizeof(XAsyncBlock));
b->context = this;
b->queue = queue;
b->callback = [](XAsyncBlock* async)
{
    int64_t remainingQuota;
    if(SUCCEEDED(XGameSaveGetRemainingQuotaResult(async, &remainingQuota)))
    {
        printf("Remaining quota: %irn", remainingQuota);
    }
    delete async;
};
XGameSaveGetRemainingQuotaAsync(providerHandle, b);
```

Todos los [XAsyncBlocks](/reference/system/xasync/structs/xasyncblock) requieren una cola de tareas (descrita a continuación), que controla
dónde y cómo se ejecuta la llamada asincrónica. Se usa una cola de tareas de todo el proceso
si no se proporciona ninguna.

Tenga en cuenta que el [XAsyncBlock](/reference/system/xasync/structs/xasyncblock) debe permanecer en memoria durante toda la duración de la
llamada asincrónica. En este ejemplo, se asignó dinámicamente y se eliminó en
la devolución de llamada de finalización. También podría almacenarse como una variable global o de
miembro. Se produce un comportamiento indefinido si el mismo [XAsyncBlock](/reference/system/xasync/structs/xasyncblock) se usa para
más de una llamada asincrónica a la vez.

[XGameSaveGetRemainingQuotaResult](/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult) completa el ciclo de una llamada asincrónica.
Libera los datos internos del bloque asincrónico, por lo que el bloque ahora puede
usarse para una nueva llamada. Las llamadas posteriores a
[XGameSaveGetRemainingQuotaResult](/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult) generan un error.
[XGameSaveGetRemainingQuotaAsync](/reference/system/xgamesave/functions/xgamesavegetremainingquotaasync) y [XGameSaveGetRemainingQuotaResult](/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult) también
están emparejados dentro del bloque asincrónico: se produce un error si
combina incorrectamente una llamada asincrónica con otra API de resultados.

Si una llamada asincrónica no tiene carga de datos, es decir, solo importa el estado HRESULT,
defina un método **Result** que tome solo el bloque asincrónico, como se muestra a continuación.

```c++ theme={null}
HRESULT QueryUpdateStatusAsyncResult(_Inout_ XAsyncBlock* block);
```

<a id="controlling_work_dispatching" />

### Control de la distribución del trabajo

¿Qué subproceso realizó el trabajo asincrónico en las llamadas anteriores? ¿Qué subproceso invocó
la devolución de llamada de finalización? Eso lo decide la cola de tareas asignada al
[XAsyncBlock](/reference/system/xasync/structs/xasyncblock).

Las colas de tareas tienen dos "puertos": un *puerto de trabajo* y un *puerto de finalización*. Cada
puerto tiene un modo de distribución que determina cómo se procesan las devoluciones de llamada en cola en un
puerto. Hay varios modos de distribución.

* **Grupo de subprocesos:** las devoluciones de llamada puestas en cola en una cola de grupo de subprocesos se
  ejecutan en el grupo de subprocesos del sistema. El grupo de subprocesos invoca las
  llamadas en paralelo, tomando por turnos una llamada de la cola para ejecutarla
  a medida que los subprocesos del grupo quedan disponibles.

* **Grupo de subprocesos serializado:** las devoluciones de llamada se ponen en cola y se ejecutan en el
  grupo de subprocesos, pero de una en una.

* **Manual:** las devoluciones de llamada puestas en cola en una cola manual no se distribuyen
  automáticamente. Corresponde al desarrollador distribuirlas en el subproceso
  que desee.

* **Inmediato:** el modo de distribución inmediato no pone nada en cola.
  Ejecuta inmediatamente la llamada en el subproceso que envió la
  devolución de llamada.

Hay una cola de tareas de proceso predeterminada configurada, de modo que tanto los puertos de trabajo
como los puertos de finalización se distribuyen a través del grupo de subprocesos del sistema. Esta
cola de tareas de proceso se usa si no se pasa ningún parámetro de cola en el
[XAsyncBlock](/reference/system/xasync/structs/xasyncblock). Un juego también puede deshabilitar la cola de tareas del proceso, lo que requiere que
se pase una cola en el [XAsyncBlock](/reference/system/xasync/structs/xasyncblock).

Nuestra expectativa es que muchos desarrolladores elijan el modo de distribución manual para
ejercer un control completo sobre cuándo y dónde se ejecutan el trabajo asincrónico y las devoluciones de llamada de
finalización.

Para obtener más información sobre las colas de tareas, consulte [Diseño de colas de tareas asincrónicas](/build/core-features/common/async/async-task-queue-design).

## Notificaciones

Una notificación podría no tener final y podría llamarse muchas veces.
Las notificaciones deben admitir un subconjunto de los requisitos de una llamada
asincrónica.

1. Asincrónica con sondeo
2. Asincrónica con devoluciones de llamada
3. Control sobre el subproceso en el que se producen las devoluciones de llamada

Las notificaciones usan una cola de tareas para permitir que el desarrollador controle
el subproceso de devolución de llamada pero, por lo demás, no usan bloques asincrónicos: están
diseñadas para parecerse más a eventos estándar con métodos **Register** y **Unregister**.

* Un método **Register** que toma los parámetros específicos de la llamada, una cola de
  tareas, un contexto void opcional y un puntero de devolución de llamada
  fuertemente tipado. El último parámetro es un parámetro *out* que
  devuelve un token.

* Un método **Unregister** que toma el contexto específico de la llamada y el
  token.

* El sondeo se admite agregando un método independiente no relacionado con la
  devolución de llamada de la notificación.

Veamos el ejemplo siguiente, que podría capturar mensajes de Windows.

```c++ theme={null}
struct XTaskQueueRegistrationToken;

typedef void MessageAvailableCallback(void* context, const MSG* msg);

HRESULT RegisterMessageAvailable(
    XTaskQueueHandle queue,
    void* context,
    MessageAvailableCallback* callback,
    XTaskQueueRegistrationToken * token);

bool UnregisterMessageAvailable(XTaskQueueRegistrationToken token, bool
wait);

// Usage.
XTaskQueueRegistrationToken token;
RegisterMessageAvailable(queue, nullptr, [](void*, const MSG* msg)
{
    printf("Message: %drn", msg->message);
}, &token);
```

Tenga en cuenta que, en este ejemplo, **UnregisterMessageAvailable** toma un parámetro
final "wait" y devuelve un bool. Esto permite a los autores de llamadas decidir cómo
controlar la anulación del registro mientras se está invocando una llamada.

<a id="heading-7" />

## Biblioteca asincrónica

Para facilitar la creación de API coherentes que admitan el patrón
asincrónico, proporcionamos una biblioteca que puede usarse para implementar la "fontanería
asincrónica" de una API. La API de la biblioteca tiene el siguiente aspecto.

```c++ theme={null}
enum class XAsyncOp : uint32_t
{
    Begin,
    DoWork,
    GetResult,
    Cancel,
    Cleanup
};

struct XAsyncProviderData
{
    XAsyncBlock* async;  
    size_t bufferSize;  
    void* buffer;  
    void* context;
};

typedef HRESULT CALLBACK XAsyncProvider(
_In_ XAsyncOp op,
_Inout_ XAsyncProviderData* data);

STDAPI XAsyncBegin (
_Inout_ XAsyncBlock* asyncBlock,
_In_opt_ void* context,
_In_opt_ void* identity,
_In_opt_ const char* identityName,
_In_ XAsyncProvider* provider);

STDAPI XAsyncSchedule(
_Inout_ XAsyncBlock* asyncBlock,
_In_ uint32_t delayInMs);

STDAPI_(void) XAsyncComplete(
_Inout_ XAsyncBlock* asyncBlock,
_In_ HRESULT result,
_In_ size_t requiredBufferSize);

STDAPI XAsyncGetResult(
_Inout_ XAsyncBlock* asyncBlock,
_In_opt_ void* identity,
_In_ size_t bufferSize,
_Out_writes_bytes_opt_(bufferSize) void* buffer,
_Out_opt_ size_t* bufferUsed);
```

Esta API usa una única devolución de llamada, combinada con un valor de operación que
indica por qué se llama a la API. También hay una única estructura de datos
que se rellena a medida que avanza la llamada. Para usar esta API,
haga lo siguiente.

1. Llame a [XAsyncBegin](/reference/system/xasyncprovider/functions/xasyncbegin) con el bloque asincrónico pasado por el autor de la llamada y
   proporcione una devolución de llamada que proporcione la implementación.

2. Realice el trabajo asincrónico de la llamada. Si necesita ejecutar el trabajo en
   un subproceso de trabajo, llame a [XAsyncSchedule](/reference/system/xasyncprovider/functions/xasyncschedule). Si puede realizar el trabajo
   usando primitivas asincrónicas del sistema operativo y configurar esas primitivas con la rapidez suficiente para
   seguir siendo seguro para tiempo crítico, esa opción es preferible.

3. Si necesita invocar otro trabajo asincrónico desde una devolución de llamada de subproceso de
   trabajo, puede devolver E\_PENDING desde el trabajo. También puede
   llamar a [XAsyncSchedule](/reference/system/xasyncprovider/functions/xasyncschedule) desde dentro de un trabajo para volver a programar trabajo
   adicional.

4. Cuando todo el trabajo esté completo, llame a [XAsyncComplete](/reference/system/xasyncprovider/functions/xasynccomplete).

5. Proporcione un contenedor fuertemente tipado alrededor de [XAsyncGetResult](/reference/system/xasyncprovider/functions/xasyncgetresult) para devolver
   los resultados.

6. Si su llamada asincrónica no tiene carga de datos, debe proporcionar un contenedor
   fuertemente tipado alrededor de [XAsyncGetStatus](/reference/system/xasync/functions/xasyncgetstatus) y pasar cero como tamaño de búfer
   requerido a [XAsyncComplete](/reference/system/xasyncprovider/functions/xasynccomplete).

La devolución de llamada del proveedor asincrónico se invoca con las siguientes
operaciones.

* **Begin**: se invoca un proveedor asincrónico con este código de operación durante [XAsyncBegin](/reference/system/xasyncprovider/functions/xasyncbegin). Si el proveedor implementa este código de operación, debe iniciar su tarea asincrónica llamando a [XAsyncSchedule](/reference/system/xasyncprovider/functions/xasyncschedule) o mediante medios externos. Se llama a esta devolución de llamada de forma sincrónica en la cadena de llamadas de [XAsyncBegin](/reference/system/xasyncprovider/functions/xasyncbegin), por lo que nunca debe bloquearse.

* **DoWork**: se llama en los casos en que se llamó a [XAsyncSchedule](/reference/system/xasyncprovider/functions/xasyncschedule) para
  programar el trabajo asincrónico mediante la cola de tareas. La función del proveedor realiza
  el trabajo que necesite. Cuando se completa, llama a [XAsyncComplete](/reference/system/xasyncprovider/functions/xasynccomplete) con el
  código de resultado y el tamaño de la carga de datos, que puede ser cero si la llamada no tiene
  carga de datos. Si es necesario realizar más trabajo asincrónico,
  el proveedor puede programar ese trabajo y debe devolver
  E\_PENDING.

* **GetResult**: se llama para capturar el resultado de la llamada. Dado que el
  tamaño de los datos se pasa a [XAsyncComplete](/reference/system/xasyncprovider/functions/xasynccomplete) durante la finalización de la llamada, aquí no se necesita
  comprobar los argumentos: la biblioteca ha verificado todos los búferes y los tamaños de
  búfer.

* **Cancel**: se llama cuando el usuario cancela una llamada asincrónica. Si la llamada puede cancelarse, cancélela y llame a [XAsyncComplete](/reference/system/xasyncprovider/functions/xasynccomplete) con E\_ABORT como código de resultado.

* **Cleanup**: se llama cuando la llamada ha finalizado completamente, y el
  proveedor puede eliminar cualquier memoria dinámica.

Un proveedor asincrónico solo necesita implementar las operaciones que necesita. Por
ejemplo, una E/S asincrónica no cancelable que no requiere limpieza solo necesita
implementar **GetResult**.

A continuación se muestra un ejemplo de un método **FactorialAsync** que implementa el factorial
de forma asincrónica.

```c++ theme={null}
UINT64 Factorial(UINT64 value)
{
    UINT64 result = 1;

    while (value != 0)
    {       
        result *= value;
        value--;
    }

    return result;
}

HRESULT FactorialAsync(UINT64 value, XAsyncBlock* async)
{
    struct CallData
    {
        UINT64 value;
        UINT64 result;
    };

    CallData* data = new CallData();
    data->value = value;
    data->result = 1;

    HRESULT hr = XAsyncBegin (async, data, FactorialAsync, __FUNCTION__, []
        (XAsyncOp op, XAsyncProviderData* data)
    {
        CallData* d = (CallData*)data->context;

        switch (op)
        {
        case XAsyncOp::Begin:
            return XAsyncSchedule(data->async, 0);

        case XAsyncOp::Cleanup:
            delete d;
            break;

        case XAsyncOp::GetResult:
            CopyMemory(data->buffer, &d->result, sizeof(UINT64));
            break;
 
        case XAsyncOp::DoWork:
            data->result = Factorial(data.Value);
            XAsyncComplete(data->async, S_OK, sizeof(UINT64));
            break;
        }

        return S_OK;
    });

    return hr;
}

HRESULT FactorialAsyncResult(XAsyncBlock* async, UINT64* result)
{
    return XAsyncGetResult(async, FactorialAsync, sizeof(UINT64), result);
}
```

## Documentación de referencia de la API

* [XAsync (contenido de la API)](/reference/system/xasync/xasync_members)
  * Funciones
    * [XAsyncGetStatus](/reference/system/xasync/functions/xasyncgetstatus)
    * [XAsyncGetResultSize](/reference/system/xasync/functions/xasyncgetresultsize)
    * [XAsyncCancel](/reference/system/xasync/functions/xasynccancel)
    * [XAsyncCompletionRoutine](/reference/system/xasync/functions/xasynccompletionroutine)
    * [XAsyncRun](/reference/system/xasync/functions/xasyncrun)
  * Estructuras
    * [XAsyncBlock](/reference/system/xasync/structs/xasyncblock)
* [XAsyncProvider (contenido de la API)](/reference/system/xasyncprovider/xasyncprovider_members)
  * Funciones
    * [XAsyncGetResult](/reference/system/xasyncprovider/functions/xasyncgetresult)
    * [XAsyncBegin](/reference/system/xasyncprovider/functions/xasyncbegin)
    * [XAsyncSchedule](/reference/system/xasyncprovider/functions/xasyncschedule)
    * [XAsyncComplete](/reference/system/xasyncprovider/functions/xasynccomplete)
* [XTaskQueue (contenido de la API)](/reference/system/xtaskqueue/xtaskqueue_members)
  * Funciones
    * [xtaskqueuecreate](/reference/system/xtaskqueue/functions/xtaskqueuecreate)
    * [xtaskqueuedispatch](/reference/system/xtaskqueue/functions/xtaskqueuedispatch)
* [xgamesave (contenido de la API)](/reference/system/xgamesave/xgamesave_members)
  * Funciones
    * [XGameSaveGetRemainingQuotaAsync](/reference/system/xgamesave/functions/xgamesavegetremainingquotaasync)
    * [XGameSaveGetRemainingQuotaResult](/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult)

## Consulte también

[Objetivos de diseño y mejoras de la programación asincrónica](/build/core-features/common/async/async-whitepaper)
[Diseño de colas de tareas asincrónicas](/build/core-features/common/async/async-task-queue-design)


## Related topics

- [Características comunes del GDK](/es/build/core-features/common/common-features-overview.md)
- [Contenedores de API de C# de Unity para el GDK](/es/build/gdk-and-engines/unity/unity-api-wrappers.md)
- [Modelo de programación asincrónica en el GDK de XBOX](/es/build/core-features/common/async/index.md)
- [Información general sobre programación asincrónica](/es/build/core-features/common/async/async-toc.md)
- [Inicio rápido de iOS](/es/services/playfab/sdks/c/quickstart-ios.md)
