导出异步等待变量并在 JS 中完成“等待”后将其导入另一个文件

Posted

技术标签:

【中文标题】导出异步等待变量并在 JS 中完成“等待”后将其导入另一个文件【英文标题】:Export an async await variable and import it to another file once its done "awaiting" in JS 【发布时间】:2021-05-14 18:25:19 【问题描述】:

我刚开始用 JS 编码为我的脚本创建一个网站,但我遇到了一些菜鸟问题。

我正在通过连接到 fetch 函数的异步等待函数更新变量。我想在另一个脚本中渲染我的变量,这样做会让人头疼。

这是我目前所拥有的,但似乎第二个文件正在导入 nul 变量。

这是我的计算和变量声明文件:

var url = 'https://financialmodelingprep.com/api/v3/profile/'+tickersymb+'?apikey='+api
var priceStat = "Working..."
var jsonData

function checkStats(url, callback) 
    return fetch(url)
        .then((response) =>  
            return response.json().then((data) => 
                console.log(data);
                return data;
            ).catch((err) => 
                console.log(err);
            ) 
        );


(async () => 
    jsonData = await checkStats(url)
    priceStat = jsonData.[0].price
    exports.jsonData = jsonData
    exports.priceStat = priceStat
    exports.tickersymb = tickersymb
)();

这是我的渲染脚本:

var compute = require('components/compute-engine');

var pricestat = compute.pricestat;
var tickersymb = compute.tickersymb;
var jsonData = compute.jsonData;

export default function HeaderStats() 
  return (
    <>
      /* Header */
      <div className="relative bg-blue-600 md:pt-32 pb-32 pt-12">
        <div className="px-4 md:px-10 mx-auto w-full">
          <div>
            /* Card stats */

            <div className="flex flex-wrap">
              <div className="w-full lg:w-6/12 xl:w-3/12 px-4">
                <CardStats
                  statSubtitle=""
                  ticker= tickersymb
                  exchange="NASDAQ"
                  statIconName="fas fa-dollar-sign"
                  statIconColor="bg-green-500"
                />
...

【问题讨论】:

您无法将异步代码转换为同步代码。这些出口也必须是承诺,进口商必须等待它们。 感谢您的回复!我如何让进口商等待? 要么导出promise本身,要么导出一个返回promise的函数。然后使用 await.then() 等待承诺(或返回的承诺) 【参考方案1】:

没有办法像这样将异步代码转换为同步代码,无论是跨模块还是在文件内。一旦进入异步链,您将永远无法将控制权重新交给同步代码,因为所有同步调用堆栈帧都需要在异步函数有机会解决之前完成。

因此,您需要导出一个 promise,然后在导入器代码中 await 它。

您可以应用一些模式,如果没有看到更大的设计上下文,我不确定哪种模式最好,但我不会尝试对这些单独的变量中的每一个使用承诺——它不会使感觉你是否想一起使用它们(Promise.all 可以做到这一点,但它不是一个对客户端友好的界面)。

相反,我建议导出一个函数,或者直接导出checkStats,或者使用一个包装器进行索引,以便为消费者整齐地准备数据,返回打包到一个对象中的数据。

看来您的意图是只获取一次数据。如果您担心该函数会在每次重新渲染时继续访问 API,那么一旦一个承诺得到解决,进一步调用 await 该承诺只会返回旧值而不是重新计算它。在下面的示例代码中,我只使用了一个 Promise 并将其存储在一个变量中,因此无论 getStats 被调用多少次,fetch 都只会被调用一次。

您的导入器应在等待承诺解决时提供默认值(您通常可以将它们导出为单独的同步变量,但为什么数据获取模块要关心 UI/视图问题?我会将其保留在导入器中)。

最后,看起来您正在使用 React,因此组件需要以某种效果使用这些数据,而不是在组件外部的同步代码中。如果您只想获取一次数据,请使用[] 作为useEffect 的第二个参数或将数据缓存在闭包中。

这里有一个玩具例子来说明:

<script type="text/babel" defer>

/**** mocks ****/
const fetch = async () => 
  console.log("fetch call made");
  return 
    json: () => new Promise(resolve => 
      setTimeout(() => resolve([price: 42]), 1000)
    )
  ;
;
const exports = ;
const require = () => exports;

/**** exporter file ****/
const url = "https://www.example.com";

const checkStats = url =>
  fetch(url)
    .then(response => response.json())
    .catch(err => console.error(err))
;

// cache the promise
const stats = checkStats(url)
  .then(jsonData => (price: jsonData[0].price))
;
exports.getStats = () => stats;

/**** importer file ****/
const useState, useEffect = React;
const getStats = require("./your-other-module");

const HeaderStats = () => 
  const [price, setPrice] = useState("loading...");

  useEffect(() => 
    getStats().then(data => setPrice(data.price));
  , [price]);

  return (<h1>price</h1>);
;

// show that even with 2 renders, fetch is 
// only called once (open browser console)
ReactDOM.render(
  [<HeaderStats />, <HeaderStats />],
  document.body
);

</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

【讨论】:

以上是关于导出异步等待变量并在 JS 中完成“等待”后将其导入另一个文件的主要内容,如果未能解决你的问题,请参考以下文章

如何等待多个异步功能完成?

我可以将变量发送到贝宝,并在付款完成后将其发回给我吗?

在启动 Angular JS 之前等待异步任务完成

异步等待不等待 useEffect 内的响应

等待 Dart 异步函数完成

js等待异步执行完成后在执行