瀏覽器在網頁中都是使用同一個執行緒執行 JavaScript 語法當一個功能需要花費較長時間時,非同步的寫法就…
callback function
callback function 是 Javascript 傳統的非同步處理方式
意思就是將一個 function 當成參數傳入,等取得資料或運算完成後
再呼叫 callback function處理後續事項
下面程式我們模擬從遠端伺服器取得資料,取得資料後(等待一秒後),呼叫 callback function 印出資料
function fetchData(callback) {
setTimeout(() => {
const data = 'Hello, world!';
// 在異步操作完成後呼叫回呼函式
callback(null, data); // 傳遞錯誤參數為 null,資料參數為 data
// 若要模擬錯誤,可以使用以下程式碼
// callback(new Error('Failed to fetch data'), null);
}, 1000);
}
function handleData(error, data) {
if (error) {
console.error('Error:', error);
} else {
console.log('Data:', data);
console.log('data Fetched');
}
}
console.log('Fetching data...');
fetchData(handleData);
console.log('end');上面程式中,最終瀏覽器 console 印出來的結果順序如下
代表執行緒遇到非同步函式時,並不會等待非同步函式執行完畢,而是直接往下執行
因此,若有需要操作資料的部分,都需要寫在 callback 裡面才能確保資料已取得
Fetching data...
callback.html:25 end
callback.html:16 Data: Hello, world!
callback.html:17 data Fetched
實務上,我們也很少特地定義一個 function 來傳入,通常都是把 function 直接寫再參數內
function fetchData(callback) {
setTimeout(() => {
const data = 'Hello, world!';
// 在異步操作完成後呼叫回呼函式
callback(null, data); // 傳遞錯誤參數為 null,資料參數為 data
// 若要模擬錯誤,可以使用以下程式碼
// callback(new Error('Failed to fetch data'), null);
}, 1000);
}
function handleData(error, data) {
if (error) {
console.error('Error:', error);
} else {
console.log('Data:', data);
console.log('data Fetched');
}
}
console.log('Fetching data...');
fetchData(fetchData((error, data)=>{
if (error) {
console.error('Error:', error);
} else {
console.log('Data:', data);
}
}););
console.log('end');以上是 callback function 簡單介紹,需注意的是若 callback function 層層堆疊時,會讓排版變的非常混亂,造成 callback hell ,這邊我就不放波動拳的梗圖了
Promise
Promise 是一個用來改善 callback function 寫法的物件,使程式碼更容易閱讀和維護
Promise 物件包含以下幾種屬性
- State:Promise 有三種狀態,分別是
pending、fulfilled、rejected,剛被建立出來的時候狀態是 pending,隨後,若程式執行成功並resolve執行結果,則狀態會變更為 fulfilled,相反的,若程式執行失敗,透過reject回傳錯誤訊息,則狀態變更為 rejected - resolve 與 reject:Promise 可以透過 resolve 或 reject 來解析並回傳結果或錯誤訊息
- .then():當 Promise 物件使用 resolve 來解析並回傳資料時,則可以透過 .then() 來獲取回傳的資料,相當於把上面callback function的內容移到這裡面來
- .catch():當 Promise 物件使用 reject 來解析並回傳錯誤訊息時,則會進入這個區塊,用來處理錯誤維護
- .finally():無論是 resolve 或 reject 都會進入的區塊,順序在 .then() 或 .catch() 之後
下面我們就來把上面 callback function 的寫法改為 Promise 寫法
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = 'Hello, world!';
// 在異步操作完成後解析 Promise
resolve(data);
// 若要模擬錯誤,可以使用以下程式碼
// reject(new Error('Failed to fetch data'));
}, 1000);
});
}
console.log('Fetching data...');
const promise = fetchData();
console.log(promise)
promise
.then(data => {
console.log('Data:', data);
console.log('Data Fetched');
console.log(promise)
})
.catch(error => {
console.error('Error:', error);
console.log(promise)
})
.finally(() => {
console.log('end');
});
console.log('main thread end');可以看到 fetchData() 改為回傳 Promise 物件
若資料取得成功,則會進入 promise.then 區塊內
而若是將第7行註解起來,第9行打開,則會進入 promise.catch 區塊內
不管是進入 .then 或是 .catch,最終都還會執行 .finally 後才結束程式
依照上面的程式,瀏覽器 print 出來的結果如下,依然可以發現程式並不會等待非同步函式執行結束後才往下走,而是先往下執行,待非同步函式完成後,才回頭去跑 .then()、.catch()、.finally()內的程式
Fetching data...
promise.html:18 Promise {<pending>}
promise.html:35 main thread end
promise.html:22 Data: Hello, world!
promise.html:23 Data Fetched
promise.html:24 Promise {<fulfilled>: 'Hello, world!'}
promise.html:31 end而如果是將第7行註解,第9行打開,則會顯示下面訊息
Fetching data...
promise.html:18 Promise {<pending>}
promise.html:35 main thread end
promise.html:27 Error: Error: Failed to fetch data
at promise.html:9:14
(anonymous) @ promise.html:27
Promise.catch (async)
(anonymous) @ promise.html:26
promise.html:28 Promise {<rejected>: Error: Failed to fetch data
at file:///C:/Users/Vic/Desktop/promise.html:9:14}
promise.html:31 endasync/await
從 promise 又延伸出 async 與 await 的兩個新特性,其實本質上是更簡便的語法糖。
async:宣告一個函式是非同步函式( async function ),並且這個函式所回傳的一定是 Promise 物件,非同步函式內部可以使用await關鍵字,當遇到await時,將暫停執行,等待 Promise 回傳結果( resolved 或 rejected ) 後,才繼續執行後續的程式碼。- await:用來等待 Promise 物件完成。可以放在非同步函式內,並且可以將 Promise 的結果賦值給一個變數
下面我們來看看將上面程式改寫為 async/await 後的寫法
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = 'Hello, world!';
// 在異步操作完成後解析 Promise
resolve(data);
// 若要模擬錯誤,可以使用以下程式碼
// reject(new Error('Failed to fetch data'));
}, 1000);
});
}
async function main() {
try {
console.log('Fetching data...');
const data = await fetchData();
console.log('Data:', data);
console.log('Data Fetched');
} catch (error) {
console.error('Error:', error);
} finally {
console.log('end');
}
}
main();
console.log('main thread end');下面來看看這個範例,瀏覽器 print 出內容的順序為何
Fetching data...
async.html:29 main thread end
async.html:18 Data: Hello, world!
async.html:19 Data Fetched
async.html:23 end與上面大同小異,印完 Fetching data… 後,因為函式內需暫停一秒,所以執行緒就先跳出往下執行 main thread end,待取得 17 行的結果後,才執行 18、19 行程式。
需注意的是,因為 main() 被宣告為 async,async function 一定會回傳一個 promise 物件,實務上能夠使用 promise = main() 來承接,接著在透過 .then() 之類的方式來處理後續。
以上是常見的幾種 JavaScript 處理非同步 function 的寫法,記錄下來供未來的自己參考
Great insights on optimizing strategies-tyy.AI Tools is a game-changer for finding solutions like the AI Ad Creative Assistant. Definitely streamlines the decision-making process for professionals.
Great insights on optimizing strategies-tyy.AI Tools is a game-changer for finding solutions like the AI Ad Creative Assistant. Definitely streamlines the decision-making process for professionals.
188betting, eh? Been having a punt on there for a while actually. Get decent odds and haven’t had any dramas cashing out. Just saying! 188betting
Interesting read! Understanding player psychology is key to any edge. Seeing platforms like 1plus ph casino app focus on VIP experiences & fast deposits (15 sec!) shows they get what serious players value. Good stuff!
Interesting read! Understanding player psychology is key to any edge. Seeing platforms like 1plus ph casino app focus on VIP experiences & fast deposits (15 sec!) shows they get what serious players value. Good stuff!