前言
Promise 的使用時機就是適合來解決非同步的事件,非同步的事件通常都是 ajax 的事件,
但其實除了 ajax 以外還有其他的方式。
ajax 示範
可以看到這個是很簡易的 jQuery ajax 的寫法,我只有撰寫了 url 向遠端位置請求資料,然後再把資料印出來。
const url = 'https://randomuser.me/api/';
$.ajax({
url: url
}).done(function (res) {
console.log(res);
});
當然 jQuery 官網也有很多種寫法,例如:直接在屬性中添加 success 屬性,或是加入不同的 Content-Type 的等等。
$.ajax({
url: 'https://randomuser.me/api/',
dataType: 'json',
success: function(data) {
console.log(data);
}
});
但目的都是為了從遠端取得資料回來,那麼我們再來看看關注現在要打的這支 API 是做什麼
可以看到這個就是你針對他們的 API 要資料以後,他會回傳給你 result 的 json 格式資料,內容就如同他們官網的顯示,
會隨機產生使用者資料給你。
那我們實際來執行看看:
的確就跟預期的一樣,出現官網上所說的資料格式
狀況1
我們來修改一下程式碼:
const url = 'https://randomuser.me/api/';
let data = {};
$.ajax({
url: url,
}).done(function (res) {
console.log(res);
data = res.results;
});
console.log(data);

可以看到 data 的資料還是維持空物件
原因很簡單,因為非同步的事件會先放到事件佇列,等其他同步的程式碼執行完之後,再依順序執行。所以這邊 ajax 事件雖然
有先放到事件佇列中,但還沒跟遠端取回資料,自然就還沒有辦法在最後面的 log 給印出來。
那麼要如何才能正確印出來呢?
最快速簡單的做法就是把 log 移進去 done 的 function Block 裡面。
const url = 'https://randomuser.me/api/';
let data = {};
$.ajax({
url: url,
}).done(function (res) {
console.log(res);
data = res.results;
console.log(data);
});
其實這樣寫,每次重新整理你會發現都會取得隨機不同的資料,如果我希望再取得第一次資料以後,又再取得同一個人的資料的
話該怎麼辦呢?我們可以看到他的官方文件中,有一個可以帶入 seed 的參數,也就是每一筆使用者資料,都有一個特定的
seed 參數。
狀況2
我們需要在第一次取得資料之後,又需要在執行完之後取得第二次的資料,那我們的寫法就會是:
const url = 'https://randomuser.me/api/';
let data = {};
$.ajax({
url: url,
}).done(function (res) {
console.log(res);
data = res.results;
const seed = res.info.seed;
console.log('seed', seed);
console.log('data', data);
console.log('res', res);
$.ajax({
url: `${url}?seed=${seed}`,
}).done(function (res2) {
console.log('res2', res2);
const res2Seed = res2.info.seed;
console.log(seed === res2Seed);
});
});
這樣就可以完成我們的需求,但又要第三次呢?第四次呢?如果我們要做特定的處理,就必須等到非同步結束以後才能進行,
這樣會造成越來越多巢狀以及程式碼繁亂不好維護的問題,而使用 Promise 的好處之一就是可以解決這個問題,來簡單介紹一
下,非同步的常見問題:
- 回呼地獄
(Callback Hell) - 寫法不一致
- 無法同時執行 (
jQuery有並行的寫法,但並不直覺)
axios 示範
我們使用 axios 這個 Promise 套件進行剛剛取得遠端資料的效果並同時解決上述非同步常見的問題
axios.get(url)
.then((res) => {
console.log(1, res);
});
當使用 axios 的時候首先要先決定要使用 get 還是 post,之後最簡單的一樣就是只帶入 url ,然後利用 then 這個方法去
串接,確保非同步行為已經完成。
- 寫法一致、避免回呼地獄:
如果要像剛剛一樣,在第一次取完以後要進行第二次非同步事件,Promise 的行為會先 return 第二次的請求開頭,
並且在下一次的 then 做接收。
axios.get(url)
.then((res) => {
console.log(1, res);
const seed = res.data.info.seed;
return axios.get(`${url}?seed=${seed}`);
})
.then((res) => {
console.log(2, res);
});
- 同時發出請求
Promise 裡面有一個 all 的語法,會同時分別針對不同的 url 進行請求,並且等到都完成拿到結果之後,再進行下一步。
Promise.all([axios.get(url), axios.get(url)])
.then(([res1, res2]) => {
console.log(1, res1);
console.log(2, res2);
});
透過陣列的方式傳入非同步行為,並且回傳一個陣列包含兩個非同步行為的結果。