Ting's Square 程式寫作之路

[Day23] JS: javascript AJAX

Total : 2560 words.

AJAX (Asynchronous JavaScript and XML)
非同步的JavaScript and XML

AJAX有以下的特性:

  • 使用 JavaScript 為主要程式語言
  • 使用 DocumentObjectModel 的方式編修HTML
  • 使用 XML 做為資料交換的格式
  • 使用 XHTML 與 CSS 以呈現資料
  • 使用 XMLHttpRequest 物件來傳輸與接收資料
    (Ref.)

目錄:


同步與非同步

同步: 打電話。需要等對方回應才能進行下一個步驟
非同步:傳訊息。不需要等對方回應,可以先去做其他的事情

同步案例:乒乓球

1.打乒乓球必須一來一回

function ping() {
	console.log('ping!');
}

function pong() {
	console.log('pong!');	
}

function play() {
	console.log('Go!');
	ping();
	pong();
	console.log('Game Set!');	
}
play();

console

"Go!"
"ping!"
"pong!"
"Game Set!" //按照順序

2.加入延遲時間的情況,其結果並沒有按照順序

function ping() {
	console.log('ping!');
	setTimeout(() => {
		console.log('殺球!!!');
	}, 3000)
	//3000 mini secs = 3 secs
}

function pong() {
	console.log('pong!');
}

function play() {
	console.log('Go!');
	ping();
	pong();
	console.log('Game Set!');
}
play();

console

"Go!"
"ping!"
"pong!"
"Game Set!"
"殺球!!!" // 殺球在game set 之後出現

AJAX優點

  1. 非同步:在不更新整個頁面的情況去修改部分的資料
  2. 常見用途:API

1. 非同步語法: promise(resolve, reject)

promise()有兩個參數: 成功resolve, 失敗reject

1.resolve: 成功才做下一步.then(),失敗則不做

let killer = new Promise((resolve, reject) => {
	resolve('十萬福特');
});

killer.then( skill => { //上一步成功,才會接到這一步
	console.log(skill);
})

console

"十萬福特"

2.reject: 失敗時.then()沒反應

let killer = new Promise((resolve, reject) => {
	reject('十萬福特');
});

killer.then( skill => { //上一步成功,才會接到這一步
	console.log(skill);
})

console: 沒有印出資料

(什麼都沒有)

3.失敗時,丟參數進去.catch(error => )

let killer = new Promise((resolve, reject) => {
	reject('十萬福特');
});

killer.then( skill => { //上一步成功,才會接到這一步
	console.log(skill);
}).catch( error => {
	console.log('error = ' + error)} )

console

"error = 十萬福特"

setTimeout(),用途:等到事件完成之後再呈現資料

4.加入延遲時間的情況

let killer = new Promise((resolve, reject) => {
  setTimeout(() => {
		resolve('十萬福特');
	}, 3000)
});

killer.then( skill => { //上一步成功,才會接到這一步
	console.log(skill);
}).catch(error => {
	console.log('error = ' + error)
})

console: 三秒之後印出十萬伏特

"十萬福特"

練習: 釋放大絕招遊戲

三秒內點五次按鈕,放出大絕招 元氣玉 完成品連結 https://codepen.io/tingtinghsu/full/qwbrMr

Step 1. jQuery起手式

$(document).ready(() => {

});

Step 2. 抓hitbutton

$(document).ready(() => {
  $('#hitButton').click( evt => {

  })
});

Step 3. 點擊計數

let chi = 0;

$(document).ready(() => {
  $('#hitButton').click( evt => {
    evt.preventDefault();
    chi += 1;
    $('#hitButton').text(`集氣 (${chi} 次) `);
  })
});

Step 4. 設定 promise 函數: 成功resolve, 失敗reject

let 大絕招 = new Promise((resolve, reject) => {
  setTimeout(() => {
    if( chi >= 5 ) {
      resolve('元氣玉');
    } else {
      reject('元氣玉');
    }
  }, 3000)
})

Step5. 成功和失敗時所呈現的html

大絕招.then(招式 => {
  $('#result').html(`使用絕招:${招式}`)
}).catch(err => {
  $('#result').html(`${err}無法使用!`)
})

Step6. 整理成一行

大絕招
.then(招式 => $('#result').html(`使用絕招:${招式}`))
.catch(err => $('#result').html(`${err}無法使用!`))

小結

let chi = 0;

$(document).ready(() => {
  $('#hitButton').click( evt => {
    evt.preventDefault();
    chi += 1;
    $('#hitButton').text(`集氣 (${chi} 次) `);
  })
});

let 大絕招 = new Promise((resolve, reject) => {
  setTimeout(() => {
    if( chi >= 5 ) {
      resolve('元氣玉');
    } else {
      reject('元氣玉');
    }
  }, 3000)
});

大絕招.then(招式 => {
  $('#result').html(`使用絕招:${招式}`)
}).catch(err => {
  $('#result').html(`${err}無法使用!`)
})

2. 什麼是API

Application Programming Interface
應用程式介面

提供開發人員一些預先定義好的函式庫,可以快速使用其功能而不必了解實際運作。

常見的Web API回應格式

有 JSON / XML / CSV 這三種

JSON

JavaScript Object Notation
JavaScript的物件表示法

JSON資料格式的表示
https://jsoneditoronline.org/

物件(object)用大括號 { } 陣列(array)用中括號 [ ]

範例:

{
  "name": "Ting",
  "lastname": "Ting",
  "report": [
    {
      "subject": "Math",
      "score": 80
    },
    {
      "subject": "English",
      "score": 90
    }
  ]
}

XML

Extensible-Markup Language
可延伸標記語言 意思是:有含意的標籤取名

<news>
    <to>日本</to>
    <from>台灣</from>
    <heading>鈴木一朗要退休了!</heading>
    <body>不.....................</body>
</news>

CSV

Comma-Separated Values
逗號分隔值

"1997","Ford","E350"

練習: 製作API抓取網路資料

農產品價格 完成品:https://codepen.io/tingtinghsu/full/QPyerz

Step 1. jQuery起手式

//希望DOM元素載完再做其他的事情
$().ready(() => {

});

Step 2. fetch()

抓網路資料,使用 fetch()

抓資料網址
http://data.coa.gov.tw/Service/OpenData/FromM/FarmTransData.aspx

回傳一個Promise(). then()可以接著處理抓回來的資料

//DOM元素載完後再做其他的事情
$().ready(() => {
  fetch('http://data.coa.gov.tw/Service/OpenData/FromM/FarmTransData.aspx').then( response => { console.log(response)})
});

console 出現錯誤訊息:

Cross-Origin Request Blocked: 
The Same Origin Policy disallows reading the remote resource at http://data.coa.gov.tw/Service/OpenData/FromM/FarmTransData.aspx. 
(Reason: CORS header Access-Control-Allow-Origin missing).

出現了關於CORS的警告訊息

CORS的含義

Cross-Origin Resource Sharing
跨網域資料分享

若是抓取的網站沒有開CORS,可以

  1. 使用CORS Proxy(可能不太穩定)
  2. 用後端程式抓

改成另外的網址

//DOM元素載完後再做其他的事情
$().ready(() => {
  fetch('https://ubin.io/data/vegetables?item=香蕉').then( response => { console.log(response)})
});

console

Response { type: "cors", url: "https://ubin.io/data/vegetables?item=%E9%A6%99%E8%95%89", redirected: false, status: 200, ok: true, statusText: "OK", headers: Headers, body: ReadableStream, bodyUsed: false }

Step 3. 把抓取菜價的功能提出成為function getVegetablePrice() 可傳不同的蔬菜參數進去

$().ready(() => {
  getVegetable('香蕉');
});

function getVegetable(item) {
  fetch(`https://ubin.io/data/vegetables?item=${item}`).then( response => { 
console.log(response)
  })
}

console

Response { type: "cors", url: "https://ubin.io/data/vegetables?item=%E9%A6%99%E8%95%89", redirected: false, status: 200, ok: true, statusText: "OK", headers: Headers, body: ReadableStream, bodyUsed: false }

Step 4. 解析轉成json格式,再回傳一個promise

function getVegetable(item) {
  fetch(`https://ubin.io/data/vegetables?item=${item}`)
  .then( response => { 
    // 解析轉成json格式
    return response.json()
    // 再回傳一個promise
  })
  .then(data => {console.log(data)})
}

console

item: "香蕉"
​
prices: (9) […]
0: Object { market: "台北二", price: 23.8 }
1: Object { market: "台北一", price: 22.4 }
2: Object { market: "板橋區", price: 24.6 }
3: Object { market: "三重區", price: 24.7 }
4: Object { market: "宜蘭市", price: 22 }
5: Object { market: "台中市", price: 21.2 }
6: Object { market: "豐原區", price: 19.38 }
7: Object { market: "高雄市", price: 25 }
8: Object { market: "鳳山區", price: 23 }
length: 9

把資料做成圖表的js函式庫: Chart.js

1.installation

Chart.js:https://cdnjs.com/libraries/Chart.js
將js檔案另存新檔,放入資料夾。

2.usage

https://www.chartjs.org/docs/latest/getting-started/usage.html

html

<head>
  <script src="scripts/Chart.min.js"></script>
</head>
<body>
  <canvas id="myChart" width="400" height="400"></canvas>
  <script src="scripts/script.js"></script>
</body>

javascript

function getVegetable(item) {
  fetch(`https://ubin.io/data/vegetables?item=${item}`)
  .then( response => { 
    // 解析轉成json格式
    return response.json()
    // 再回傳一個promise
  })
  .then(data => {  renderChart(data);}) //畫圖
}

Step 5. renderchart抓取蔬果資料(變數: market/price)

ES6的 .map(): 針對某個陣列的每個元素做某件事情,再重新搜集成一個陣列

function renderChart(data) {

  let markets = data["prices"].map(p => {
    return p.market
    //等於 return price["market"]
  }) 

  //測試 console.log(markets);

  let prices = data["prices"].map(p => {
    return p["price"]
  })

  // 測試 console.log(prices);

整理更簡潔的程式碼

  let markets = data["prices"].map(p => p.market) 
  let prices = data["prices"].map(p => p.price)

最後,把抓好的資料填進去圖表!

  let title = `${data.item}價格`;
  
  var ctx = $('#myChart');  
  var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: markets,  //各地市場  
        datasets: [{
            label: title, // 標題
            data: prices, // 價格
            backgroundColor: [
                'rgba(255, 99, 132, 0.2)',
                'rgba(54, 162, 235, 0.2)',
                'rgba(255, 206, 86, 0.2)',
                'rgba(75, 192, 192, 0.2)',
                'rgba(153, 102, 255, 0.2)',
                'rgba(255, 159, 64, 0.2)',
                'rgba(155, 199, 132, 0.2)',
                'rgba(54, 162, 235, 0.2)',
                'rgba(255, 206, 86, 0.2)'                
            ],
            borderColor: [
                'rgba(255, 99, 132, 1)',
                'rgba(54, 162, 235, 1)',
                'rgba(255, 206, 86, 1)',
                'rgba(75, 192, 192, 1)',
                'rgba(153, 102, 255, 1)',
                'rgba(255, 159, 64, 1)'
            ],
            borderWidth: 1
        }]
    },
    options: {
        scales: {
            yAxes: [{
                ticks: {
                    beginAtZero: true
                }
            }]
        }
    }

相關連結

HackMD筆記
CodePen練習一
CodePen練習二
網頁版-釋放放大絕招遊戲
網頁版-API抓資料
JSON Viewer(Chrome 外掛)
Chart.js
政府資料交換平台
農產品交易行情(官方版)
農產品交易行情(特製版)