用React刻自己的投资Dashboard Day23 - 非同步呼叫API,完成首页资料串接

tags: 2021铁人赛 React

上一篇确认过API内容之後,剩下的部份就是串接API,并将资料呈现在画面上了,虽然描述起来是短短两句,不过还是写了好多的程序码,下面就会挑两个重点部份来说明。

非同步呼叫API,增进程序效能

在取得美国股市不同指数的收盘资料时,因为参数不同,每个指数都需要呼叫一次API,下面比较我刚开始的写法跟後来非同步的写法,速度差蛮多的。

  • 刚开始的写法:一支回传後再叫下一支
    这边说明一下,资料起始日的部份,我是从当天往前推4天开始都收,主要原因是为了怕捞到假日,因为美档指数都要有最近两日的资料,因为美国休市日都大概1~2天,因此4天应该是够用。
const fetchCloseData = async () => {
  # 指定资料起始日
  let parse_date = new Date(today.valueOf() - 4 * 1000 * 60 * 60 * 24).toISOString().slice(0, 10);
  
  # 一支API跑完再跑下一支
  const DJI = await FinmindAPIUS({ dataset, data_id: "^DJI", start_date: parse_date, end_date: "" })
  const GSPC = await FinmindAPIUS({ dataset, data_id: "^GSPC", start_date: parse_date, end_date: "" })
  const IXIC = await FinmindAPIUS({ dataset, data_id: "^IXIC", start_date: parse_date, end_date: "" })
  const SOX = await FinmindAPIUS({ dataset, data_id: "^SOX", start_date: parse_date, end_date: "" })
  const VIX = await FinmindAPIUS({ dataset, data_id: "^VIX", start_date: parse_date, end_date: "" })

  return ...
  
};

  • 改良後的写法:非同步执行
    这样的写法让程序一开始执行的时候,5支API请求会一起发送,而Promise.all则是会等5支API都有回应之後,才算完成,因此这样的程序一方面可以确保资料都有拿到,另一方面因为不用等前面的API回应後才执行下一支,速度会有所提升。
const fetchCloseData = async () => {
  # 指定资料起始日
  let parse_date = new Date(today.valueOf() - 4 * 1000 * 60 * 60 * 24).toISOString().slice(0, 10);
  
  # 非同步执行
  const [DJI, GSPC, IXIC, SOX, VIX] = await Promise.all([
    FinmindAPIUS({ dataset, data_id: "^DJI", start_date: parse_date, end_date: "" }),
    FinmindAPIUS({ dataset, data_id: "^GSPC", start_date: parse_date, end_date: "" }),
    FinmindAPIUS({ dataset, data_id: "^IXIC", start_date: parse_date, end_date: "" }),
    FinmindAPIUS({ dataset, data_id: "^SOX", start_date: parse_date, end_date: "" }),
    FinmindAPIUS({ dataset, data_id: "^VIX", start_date: parse_date, end_date: "" }),
  ])

  return ...
  
};

根据资料需求变更版面

如下图这两个资料,之前有提到单一资料区块已经有拉一个共用的UI模板出来,所以这张图可以看成两个类似的UI模板,不过其实又有些许的不同,加权指数那条有三个资料,而买卖超只有一个资料,但是我还是想让他们共用一个UI就好,因为长相是类似的。

作法就是当收到的资料有包含spread,就表示它是上面那个样子的模板,如果没有,就是下面的模板,因此程序码撰写如下,透过判断props传进来的资料有哪些,去return适合的版型,不过看起来JSX的部份还是蛮冗长的就是了,时间有限,就先这样子写,有时间的话应该可以切更多子件,看起来会更简洁一些。

src\UI\IndexCloseInfo\IndexCloseInfo.js

import React from 'react';
import styles from './IndexCloseInfo.module.css';
import { Col } from 'react-bootstrap';

const Card = (props) => {
  # 有spread资料
  if (props.data && props.data.close.spread) {
    return (
      <Col sm={12} md={6} lg={4} xl={3} xxl={3} className={styles.bar}>
        <div className={styles.bar_left_side}>
          <div className={styles.bar_icon}>{props.data.info.iconText}</div>
        </div>
        <div className={styles.bar_right_side}>
          <p className={styles.bar_text}>{props.data.info.name}</p>
          <div className={styles.bar_data_group}>
            <p className={`${styles.bar_data} ${props.data.close.spread > 0 && props.data.close.spread !== 0 ? styles.red_text : styles.green_text}`}>{props.data.close.price}</p>
            <p className={styles.bar_data}>|</p>
            <p className={`${styles.bar_data} ${props.data.close.spread > 0 && props.data.close.spread !== 0 ? styles.red_text : styles.green_text}`}>{props.data.close.spread}</p>
            <p className={styles.bar_data}>|</p>
            <p className={`${styles.bar_data} ${props.data.close.spread > 0 && props.data.close.spread !== 0 ? styles.red_text : styles.green_text}`}>
              {
                Math.round(props.data.close.spread * 10000 / (props.data.close.price - props.data.close.spread)) / 100
              }
              %
            </p>
          </div>
        </div>
      </Col >
    )
    # 没有spread资料
  } else if (props.data && !props.data.close.spread) {
    return (
      <Col sm={12} md={6} lg={4} xl={3} xxl={3} className={styles.bar}>
        <div className={styles.bar_left_side}>
          <div className={styles.bar_icon}>{props.data.info.iconText}</div>
        </div>
        <div className={styles.bar_right_side}>
          <p className={styles.bar_text}>{props.data.info.name}</p>
          <div className={styles.bar_data_group}>
            <p className={`${styles.bar_data} ${props.data.close.price > 0 && props.data.close.price !== 0 ? styles.red_text : styles.green_text}`}>{+(Math.round(props.data.close.price / 100000000 + "e+2") + "e-2")}亿</p>
          </div>
        </div>
      </Col >
    )
    # 没有任何资料
  } else {
    return <p>No data</p>
  }
};

export default Card;

小结

其实刚开始写的时候,没有想到首页的程序码会有这麽多,有一部分的原因可能是因为无法改变API的内容,所以需要写一些整理资料的程序,虽然画面处理的速度可能有点慢,不过还是成功串接了API,并随时显示最新资料,蛮有成就感的。


<<:  [Day24] Flutter with GetX Shimmer

>>:  Day22 Plugin 从零开始到上架 - 取得权杖(iOS)

Day8 Switch case

Switch case 是一个跟if else 相似的条件判段与陈述句,不过他只能用来比较数值或字元...

Day8 资料监听

今天要来介绍资料监听Watch~ Watch的用法是当这个值发生改变时,我们可以做出相对应的事 在w...

从零开始的8-bit迷宫探险【Level 18】为什麽他们开始乱跑?捉摸不定的怪物移动模式

「诶,好累喔。」 「天气这麽好,要不要去玩水啊?」喜欢玩耍的 Snow 提议着。 忘记要赶走山姆的...

Day28回圈(JavaScript)

回圈 JS一般使用的回圈有这几种 for 给予一定的循环次数做回圈 for/in 在指定对象的属性里...

[Day23]下载POSTMAN以及MYSQL

Postman 下载Postman:https://www.postman.com/ 下载对应版本的...