iT邦幫忙

2022 iThome 鐵人賽

DAY 8
0
自我挑戰組

SPYxFRONTEND ~ 懂一點後端真是讓人哇哭哇哭系列 第 8

[Day8] 用 fetch 拿資料好愜意

  • 分享至 

  • xImage
  •  

前言

因為昨天文章發的有點匆忙,
今天的文章會再補充一下昨天介紹的 XHR,
然後練習 fetch 的用法~

本日正文

用 XHR 好像有點麻煩?

昨天練習了用 XHR 拿資料的方法,
但是有沒有覺得它用起來有點麻煩?
前面都要先宣告 XMLHttpRequest 的物件,
然後還得寫 open 跟 send 才會真的發送請求出去:

const xhr = new XMLHttpRequest();
  xhr.open("get", sourceUrl, true);
  xhr.send(null);

再來好不容易發送請求出去了,
你還沒辦法直接取用 call API 後回傳的資料,
還得再多寫 xhr.onload
這時候才拿得到資料才能做後續的處理:

xhr.onload = function () {
		setdataResult(JSON.parse(xhr.responseText));
	};
}

而且如果是直接在 xhr.onload 裡面寫變數,
因為是在 function 內,
function 外沒辦法直接取得這個變數(作用域的關係),
例如這邊將昨天的例子改寫成這樣:

function fetchData(){
    const xhr = new XMLHttpRequest();
    xhr.open("get", sourceUrl, true);
    xhr.send(null);
    xhr.onload = function () {
      result = JSON.parse(xhr.responseText);
      console.log(result);
    };
    console.log(result);
  }

然後文字的地方改成直接取用 {result}

<Text my={4} fontSize="p" textAlign="center">
  result: {result?.records?.location?.length}
</Text>

https://ithelp.ithome.com.tw/upload/images/20220909/20129873jO8ggzphwp.png

這樣在 xhr.onload 外面其實是拿不到資料的(要另外處理),
所以昨天後來我是用 useState 去改值(將拿到的資料設定進去),
這樣是不是有夠麻煩orz

因為 XMLHttpRequest 算是很陽春的 AJAX 實作方法,
所以之後也出現了幾個好用的工具,
我覺得應該是為了克服 XHR 的一些缺點啦。

好用的 fetch

像是今天要練習的 fetch 就是其中一個,
fetch 是使用 Promise 物件的寫法,
Promise 又是什麼呢?

Promise 要認真說的話又可以獨立一篇了XD
這邊推薦可以看大大的文章 → JavaScript Promise 全介紹

我自己簡單的理解就是,
Promise 是為了優化 Javascript 的 非同步 語法部份,
例如在寫 Javascript 時你會發現你寫在前後行的語法(EX: 執行A; 執行B;),
但並不會真的照你寫的順序執行,
而是"同時"執行,
所以如果你需要寫 A執行完才要執行B 的語法,
Promise 就能解決這個問題~

前情提要講完了,
那我們接下來就看一下 fetch 的簡單寫法吧:

function fetchData(){
	fetch(sourceUrl)
	.then(function(response) {
	  return response.json();
	})
	.then(function(myJson) {
	  console.log(myJson);
	});
}

簡單說明一下,
fetch(sourceUrl) 這邊意思就是傳送要求到 sourceUrl
等到拿到 response 之後(.then) 回傳 response.json()
接著(.then) 再用 console.log 把 response.json() 印出來,
像這樣:
https://ithelp.ithome.com.tw/upload/images/20220909/20129873wx8VKePWDm.png

不需要特別寫 open, send, onload 就能完成 call API 並拿到回傳資料,
這樣是不是簡單多了呢?

既然 fetch 拿資料這麼方便,
那我們今天用拿到的資料來簡單寫一下應用好了。

首先在動手前,昨天也有提到我會習慣先觀察一下資料:
https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-C0032-001?Authorization=rdec-key-123-45678-011121314

https://ithelp.ithome.com.tw/upload/images/20220909/20129873I553oqf6lQ.png

題外話,你可能會發現資料長得很密密麻麻要怎麼看?
這邊推薦可以裝 Chrome 的擴充套件 JSON Viewer
然後它就會把 JSON 轉換成比較可讀的樣子,
像這樣:
https://ithelp.ithome.com.tw/upload/images/20220909/20129873tiB1zxrQ0j.png

從資料裡面可以看到有各縣市的明日天氣預報資料,
現在假設我明天要去臺中玩(並沒有T_T),
我想關心一下臺中明天的天氣情況,
這邊就稍微處理一下拿到的資料,
我習慣是觀察好資料結構後,先拿一筆資料試寫看看(這邊容我這邊快轉一下):

function App() {
  const sourceUrl =
    "https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-C0032-001?Authorization=rdec-key-123-45678-011121314";
  const [dataResult, setdataResult] = useState([]);
  const searchArea = '臺中市';
  const [dataSearchArea, setDataSearchArea] = useState({});
  let result = [];

  function fetchData(){
    fetch(sourceUrl)
    .then(function(response) {
      return response.json();
    })
    .then(function(myJson) {
      setdataResult(myJson.records.location);
    });
  }

  useEffect(() => {
    setDataSearchArea(dataResult.filter((d) => d.locationName === searchArea));
  }, [dataResult]);

  return (
    <ChakraProvider theme={theme}>
      <>
        <Text my={4} fontSize="p" textAlign="center">
          sourceUrl: {sourceUrl}
        </Text>
        <Text my={4} as="h2" fontSize="2xl" textAlign="center">
          全部總共 {dataResult.length === 0 ? 0 : dataResult?.length } 筆資料
        </Text>
        <Divider my={4} />
        <Text my={4} as="h3" fontSize="xl" textAlign="center">
          區域: {searchArea}
        </Text>
        <Text px={4}>
          [明日天氣預報]
          <br />
          時間:{dataSearchArea[0]?.weatherElement[0]?.time[0]?.startTime}
          {' ~ '}
          {dataSearchArea[0]?.weatherElement[0]?.time[0]?.endTime}
          <br />
          天氣:{dataSearchArea[0]?.weatherElement[0]?.time[0]?.parameter?.parameterName}
        </Text>
        <Divider my={4} />
        <Center>
          <Button onClick={fetchData}>取得資料</Button>
        </Center>
      </>
    </ChakraProvider>
  );
}

https://ithelp.ithome.com.tw/upload/images/20220909/20129873rcZBxUbSWj.png

然後從資料我們可以看到每天的預報資料有 3 筆 (在 time 裡面),
https://ithelp.ithome.com.tw/upload/images/20220909/20129873lEoSvIv0oc.png
所以先用一筆確定是你要的畫面之後,
我就會把 timemap 以迴圈的方式讀取,把 3 筆資料都渲染出來,
像這樣:

taSearchArea[0]?.weatherElement[0]?.time?.map((t) => (
    <Box>
      時間:{t.startTime}
      {' ~ '}
      {t.endTime}
      <br />
      天氣:{t.parameter?.parameterName}
    </Box>
  ))}

https://ithelp.ithome.com.tw/upload/images/20220909/20129873xKoCLZkXnu.png

這樣你就可以看到臺中市的明天天氣預報資料都出現了~
(看起來不會下雨真是太好了但我不會去臺中玩QQ)

如果去 fetch 遇到 error 怎麼辦?例外處理很重要

到這邊千萬不要高興太早,
如果你去 fetch 遇到 error (EX. 目的地server掛掉,或網址打錯...等)
那會怎樣呢?
這邊我故意把 url 多打字來試試看:
https://ithelp.ithome.com.tw/upload/images/20220909/20129873gNxDkSLiN4.png

你可以看到畫面上都沒有資料,
打開 console 來看可以看到有 error
(PS. 其實一般來說,應該是瀏覽器會直接跳 error 畫面,
所以 error 是需要另外處理的,
但可能我裝的套件有特別處理這個事件,所以畫面上很安靜什麼都沒發生)

這種情況該怎麼辦呢?
Promise 有提供 catch 的語法,
讓你設定在執行前面動作遇到 error 會執行什麼動作,
像這樣:

fetch(sourceUrl)
    .then(function(response) {
      return response.json();
    })
    .then(function(myJson) {
      setdataResult(myJson.records.location);
    })
    .catch((err) => {
      alert("抓取資料錯誤,請確認後再試");
      console.log(err);
    })

https://ithelp.ithome.com.tw/upload/images/20220909/20129873AlM7gDK0aB.png

這邊你就會看到跳出一個警告視窗顯示:抓取資料錯誤,請確認後再試,
而這個就是 Exception Handling(例外處理)
顧名思義,就是在程式未正常執行時要做什麼處理,
尤其是在跟後端介接 call API 時,例外處理就會變得很重要,
要依據後端給出的 error code 不同時要做不同的處理,
像是前端顯示不同的錯誤訊息,
讓使用者或工程師看到很快知道要怎麼處理這樣~

因此後面的篇章 catch 會常常出現,
也是我們在 call 後端 API 時一定要寫的語法,
今天介紹完 fetch 明天就讓我們進入 axios 的世界吧~~~

參考文章:

request的方式? ajax & fetch & axios

後記

今天終於比較沒那麼趕了orz
感覺好像每年中秋連假都在寫鐵人賽文章中度過XD
真充實XDDDD


上一篇
[Day7] 來到 call API 的第一道門 ~ AJAX - XMLHttpRequest 篇
下一篇
[Day9] 來認識 axios 吧!之「既生 fetch,何生 axios?」
系列文
SPYxFRONTEND ~ 懂一點後端真是讓人哇哭哇哭30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言