Код IT
← Каталог

Кнопка с загрузкой — React, Promise и поток обновлений — Поток значений — `async function*` и `yield`

Фрагмент из «Кнопка с загрузкой — React, Promise и поток обновлений»: Поток значений — `async function*` и `yield`.

javascript javascriptencyclopedia3-ecosystem-2-frontend-frameworks-1-react-45 embed URL статья в энциклопедии
Plain text main.jsx

import { useState } from 'react';

function wait(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function LoadingButton({ onClick, children, className }) {
  const [label, setLabel] = useState(children);
  const [isLoading, setIsLoading] = useState(false);

  async function handleClick() {
    if (isLoading) return;
    setIsLoading(true);
    setLabel(children);

    try {
      const result = onClick();

      if (result && typeof result[Symbol.asyncIterator] === 'function') {
        for await (const step of result) {
          setLabel(step);
        }
      } else {
        await result;
      }
    } finally {
      setIsLoading(false);
      setLabel(children);
    }
  }

  return (
    <button
      type="button"
      className={className}
      onClick={handleClick}
      disabled={isLoading}
    >
      {isLoading ? label : children}
    </button>
  );
}

function Demo() {
  return (
    <LoadingButton
      className="counter"
      onClick={async function* () {
        yield 'Starting some process';
        await wait(1000);
        yield 'Oops, taking longer than usual';
        await wait(2000);
        yield (
          <span>
            Maybe there is a <code>Retry-after</code>?
          </span>
        );
        await wait(3000);
        yield 'Nevermind, I got it';
      }}
    >
      Click me
    </LoadingButton>
  );
}

import { useState } from 'react';

function wait(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function LoadingButton({ onClick, children, className }) {
  const [label, setLabel] = useState(children);
  const [isLoading, setIsLoading] = useState(false);

  async function handleClick() {
    if (isLoading) return;
    setIsLoading(true);
    setLabel(children);

    try {
      const result = onClick();

      if (result && typeof result[Symbol.asyncIterator] === 'function') {
        for await (const step of result) {
          setLabel(step);
        }
      } else {
        await result;
      }
    } finally {
      setIsLoading(false);
      setLabel(children);
    }
  }

  return (
    <button
      type="button"
      className={className}
      onClick={handleClick}
      disabled={isLoading}
    >
      {isLoading ? label : children}
    </button>
  );
}

function Demo() {
  return (
    <LoadingButton
      className="counter"
      onClick={async function* () {
        yield 'Starting some process';
        await wait(1000);
        yield 'Oops, taking longer than usual';
        await wait(2000);
        yield (
          <span>
            Maybe there is a <code>Retry-after</code>?
          </span>
        );
        await wait(3000);
        yield 'Nevermind, I got it';
      }}
    >
      Click me
    </LoadingButton>
  );
}