使用 Promise 检查是否已生成新的随机数

Using a Promise to check if a new random number has been generated

我正在使用 Chainlink Oracles 获取随机数。这个获取随机数的过程需要一段时间。为此,我必须执行这一行(异步调用),它调用我的智能合约的一个函数并存储新的随机数:

const newRandomNumber = await contract.connect(signer).randomResult(); 

我想要的是使这个过程自动化:当生成一个新号码(并且可以使用)时,我想向用户显示一条消息(在我的前面)。

Hey, a new number has been generated.

如何定期检查 newRandomNumber 中存储的结果是否已更改?当发生这种情况时,我如何显示消息?

因为您在问题上标记了 React,所以这里有一个使用 React 状态的注释示例:

<div id="root"></div><script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/standalone@7.16.6/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="env,react">

const {useEffect, useState} = React;

// simulate making a network request to get a random number from an API:
// it will return a promise which will (at some point in the future)
// resolve with the random number, or reject with an Error
function mockFetchRandomNumber () {
  const randomNumber = Math.floor(Math.random() * 1000) + 1; // 1-1000
  const delay = Math.floor(Math.random() * 1000) + 1000; // 1-2s
  const chance = 0.2; // percent chance of random network failure
  return new Promise((resolve, reject) => setTimeout(() => {
    if (chance > Math.random()) reject(new Error('Network error'));
    else resolve(randomNumber);
  }, delay));
}

// a custom hook to wrap the logic of fetching the random number
// and maintain the state of the request while doing so
function useRandomNumber () {
  const [data, setData] = useState(); // the potential data
  const [error, setError] = useState(); // a potential error
  const [isLoading, setIsLoading] = useState(false); // whether the request is still pending
  const [reloadBoolean, setReloadBoolean] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);
      setData(undefined);
      setError(undefined);
      try {
        const randomNumber = await mockFetchRandomNumber();
        setData(randomNumber);
        setError(undefined);
      }
      catch (ex) {
        setError(ex instanceof Error ? ex : new Error(String(ex)));
        setData(undefined);
      }
      setIsLoading(false);
    };
    fetchData();
  }, [reloadBoolean, setData, setError, setIsLoading]);

  return {
    data,
    error,
    isLoading,
    // will toggle the value of `reloadBoolean`, forcing a re-render
    reload: () => setReloadBoolean(b => !b),
  };
}

function Example () {
  const {data, error, isLoading, reload} = useRandomNumber();
  return (
    <div>
      {
        isLoading
          ? (<div>Loading random number...</div>)
          : null
      }
      {
        error
          ? (<div>There was an error loading the data ({error.message})</div>)
          : null
      }
      {
        data
          ? (<div>The random number is: {data}</div>)
          : null
      }
      <button onClick={reload}>Get new number</button>
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById('root'));

</script>

tl;博士

创建一个 Observable 以定期调用 API-端点:

import { distinctUntilChanged, switchMap, timer } form 'rxjs'; /* Use RxJS library for convenience */

const getRandomNumber = contract.connect(signer).randomResult();

const observable = timer(0, 1000) /* Trigger now and each 1000 ms */
    .pipe(
        switchMap(getRandomNumber), /* On each run, get the current number */
        distinctUntilChanged(), /* Only trigger subscribers if new number */
    )

const subscription = observable.subscribe((number) => console.log(number));

/* Don't forget to unsubscribe after, e.g. when the component unmounts */
subscription.unsubscribe();

长版

观察者模式允许您接收多个异步事件的通知(e.i。'subscribe')- 与 javascript Promise 在单个事件后通知您的方式相同异步事件完成。 (观察者实际上比这更灵活,这只是一个用例)

一个天真的javascript实现

让我们看看如何在基本 javascript

中实现您想要的行为
let currentRandomNumber = null;
let isCanceled = false;

async function checkAndUpdateNumber() {
  while (!isCanceled) {
    /* Set your new (or same) number */
    currentRandomNumber = await contract.connect(signer).randomResult();

    /* Wait 1000ms */
    await new Promsie((resolve) => setTimeout(resolve, 1000));
  }
}
checkAndUpdateNumber();

/* If you want to stop the loop */
isCancelled = true;

此实现有效,但仍有很大的改进空间。代码一点都不可重用,也不容易测试。

Observers 为您提供了一个更简洁的界面来处理多个异步操作。查看 this 文章以了解观察者如何在幕后工作。

Observables 的首选 javascript 库是 RxJS。它经过充分测试并提供了无数实用方法,因此我强烈建议您检查一下。