Continuation - 2. А что если LWC?

В прошлой статье на эту тему (Continuation - что за зверь такой?) мы рассмотрели работу с Continuation из Visualforce страницы, что является привычным и, не побоюсь этого слова,
каноничным вариантом его использования с тех пор, как он стал доступен (в 15м году, если не ошибаюсь).
В релизе Summer’19 была добавлена возможность использовать Continuation и в lightning разработке. В качестве примера возьмём стильный, модный и молодёжный LWC и соорудим вэб-компонент, который будет отправлять асинхронный коллаут на удалённый сервис, ждать ответа 10 секунд и выводить ответ на UI.

Как известно, в LWC апекс метод можно вызвать не только императивно или явно, но и при помощи @wire. Рассмотрим оба варианта. Само собой, то, что к Continuation можно обратиться из лайтнинга, не отменяет того факта, что работа с ним ведётся в апекс коде. Создадим класс:

public with sharing class SampleContinuationClass {
  
  private static final String LONG_RUNNING_SERVICE_URL = 'https://postman-echo.com/delay/10';//сервис, который ответит с задержкой 10 секунд. 
//Ссылка на доки (https://docs.postman-echo.com/?version=latest#0189572f-509e-efe0-686d-eed4b3d2f1f0)
// Нет, не надо хардкодить урлы, это просто пример :) 
 
  @AuraEnabled(continuation=true)// чтобы отправить Continuation запрос
  public static Object startRequestImperative() {
    Continuation con = new Continuation(20); // создаём объект и задаёт таймаут
    con.continuationMethod = 'processResponseImperative'; // говорим ему в каком методе будет обрабатываться коллбэк
    HttpRequest req = new HttpRequest();
    req.setMethod('GET');
    req.setEndpoint(LONG_RUNNING_SERVICE_URL);
    con.addHttpRequest(req); // отдаём реквест
    return con; // объект Continuation нужно вернуть
  }

  @AuraEnabled
  public static Map<String, Object> processResponseImperative(List<String> labels, Object state) {
    HttpResponse response = Continuation.getResponse(labels[0]);
    return (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
  }
}

Где параметр labels - список уникальных лейблов, по одному на каждый запрос (объект Continuation может содержать до 3х запросов).
state - позволяет передать в метод обработки коллбэка какое-нибудь значение, задав его в методе, где формируем объект Continuation. Пример: con.state=‘Hello, World!’;

И для wired вызова добавим пару похожих методов:

@AuraEnabled(continuation=true cacheable=true)
  public static Object startRequestWire() {
    Continuation con = new Continuation(40);
    con.continuationMethod = 'processResponseWire';
    HttpRequest req = new HttpRequest();
    req.setMethod('GET');
    req.setEndpoint(LONG_RUNNING_SERVICE_URL);
    con.addHttpRequest(req);
    return con;
  }

  @AuraEnabled(cacheable=true)
  public static Map<String, Object> processResponseWire(List<String> labels, Object state) {
    HttpResponse response = Continuation.getResponse(labels[0]);
    return (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
  }

В разметке нашего компонента разделим wired и imperative по разным lightning-card-ам для наглядности:

<template>
    <lightning-layout class="x-large">
        <lightning-layout-item padding="around-small">
            <lightning-card title="Imperative">
                <p class="slds-p-horizontal_small">
                    <lightning-button label="Call Continuation" onclick={callContinuation}></lightning-button>&nbsp;
                    Imperative result: {formattedImperativeResult}
                </p>
            </lightning-card>
        </lightning-layout-item>
        <lightning-layout-item padding="around-small">
            <lightning-card title="Wire">
                <p class="slds-p-horizontal_small">Wired result: {formattedWireResult}</p>
            </lightning-card>
        </lightning-layout-item>
    </lightning-layout>
</template>

И в .js контроллере обратимся к нашим апекс методам по очереди, чтобы увидеть результаты:

import { LightningElement, track, wire } from 'lwc';
import startRequestImperative from '@salesforce/apexContinuation/SampleContinuationClass.startRequestImperative';
import startRequestWire from '@salesforce/apexContinuation/SampleContinuationClass.startRequestWire';

export default class ContinuationComponent extends LightningElement {
    @track imperativeContinuation = {};

    callContinuation() {// метод вызывается при назажии на кнопку на UI
        this.imperativeContinuation = '';
        startRequestImperative()
            .then(result => {
                this.imperativeContinuation = result;
            })
            .catch(error => {
                this.imperativeContinuation = error;
            }
        );
    }

    get formattedImperativeResult() {
        return JSON.stringify(this.imperativeContinuation);
    }


    @wire(startRequestWire)
    wiredContinuation //и второй вариант

    get formattedWireResult() {
        return JSON.stringify(
            typeof this.wiredContinuation.data !== 'undefined' ? this.wiredContinuation.data : this.wiredContinuation.error
        );
    }
}

Таким образом мы можем наблюдать, как результат асинхронного вызова отображается на UI после его возвращения от стороннего сервиса.

1 Like