【问题标题】:Using a Promise to check if a new random number has been generated使用 Promise 检查是否生成了新的随机数
【发布时间】:2022-01-24 02:34:20
【问题描述】:

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

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

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

嘿,新号码生成了。

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

【问题讨论】:

  • 您能否提供有关如何提供随机数的文档链接或显示专门获取随机数的示例代码?
  • 不清楚你在问什么;你想知道 Promise 是如何工作的吗?如何重复调用一个函数?请澄清一下,以便人们可以给您更具体的答案:)
  • @MarcRo 感谢您的帮助。我已经简化了问题。
  • @code 我不确定这是否重要。我的意思是,我只关心前端部分。该行得到一个随机数,但大约需要 2 分钟,所以我不知道生成该数字的确切时间。非常感谢。
  • @Joncarre,谢谢这让它更清楚了。我不熟悉您的智能合约的功能。它会在每次调用时生成一个新的随机数吗?还是会定期生成一个存储在链上的号码,而您的randomResult() 调用仅检索当前存储的号码?

标签: javascript reactjs promise smartcontracts chainlink


【解决方案1】:

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();

加长版

观察者模式允许您接收多个异步事件的通知(例如“订阅”) - 与 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 文章,了解观察者如何在幕后工作。

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

【讨论】:

    【解决方案2】:

    因为您在问题上标记了 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>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-31
      • 2014-02-27
      • 2019-01-09
      • 1970-01-01
      相关资源
      最近更新 更多