Photo by Mohammad Rahmani on Unsplash
使用 async / await
可以避免 callback hell
,程式可读性也比 Promise
更好,也可以达到同步处理的效果
函式说明
函式变数 | 说明 |
---|---|
Promise.resolve() | 回传资料给 then() 通知已执行完成 |
Promise.reject() | 回传资料给 catch() 通知执行失败 |
Promise.then() | 取得 resolve() 回传的资料,执行成功 |
Promise.catch() | 取得 reject() 回传的资料,执行失败 |
Promise.finally() | 整个 Promise 执行结束,不管成功失败,都会呼叫的函式 |
Promise.all() | 确认所有阵列中的 Promise 都有执行完成 |
Promise.pending | 等待 |
Promise.fulfilled | 成功 |
Promise.rejected | 失败 |
async | 告诉这个函式是 Promise 函式,可以使用 then() 及 catch() 去接收处理后的资料 |
await | 只能出现在 async 函式中,会等待指定的 Promise 函式执行完再执行 |
使用 Promise
建立 Promise 物件
const logAsync = (message, time) => {
// 建立 Promise 物件,处理异步资讯
return new Promise((resolve, reject) => {
if (message && time) {
// 设定指定时间显示讯息
setTimeout(() => {
console.log(message);
// 执行正常,呼叫 resolve 通知已处理完成
resolve(`从 resolve 取得:${message}`)
}, time);
} else {
// 处理异常,呼叫 reject 通知处理错误
reject(`无法处理 reject: ${message} & ${time}`);
}
});
};
使用 then() 取得 Promise 呼叫的 resolve
logAsync('这个讯息过 1 秒才会出现', 1000)
.then((log1_resolve_message) => {
console.log(log1_resolve_message);
return logAsync('这个讯息再过 1.5 秒才会出现', 1500);
})
.then((log2_resolve_message) => {
console.log(log2_resolve_message);
return logAsync('这个讯息再过 2 秒才会出现', 2000);
}).then((log3_resolve_message) => {
console.log(log3_resolve_message);
});
// 这个讯息过 1 秒才会出现
// 从 resolve 取得:这个讯息过 1 秒才会出现
// 这个讯息再过 1.5 秒才会出现
// 从 resolve 取得:这个讯息再过 1.5 秒才会出现
// 这个讯息再过 2 秒才会出现
// 从 resolve 取得:这个讯息再过 2 秒才会出现
使用 async / await
await
在async function
里面才可以使用
const logMessage = async () => {
let log1_resolve_message = await logAsync('1 秒后会出现这句', 1000);
console.log(log1_resolve_message);
let log2_resolve_message = await logAsync('再 1.5 秒后会出现这句', 1500);
console.log(log2_resolve_message);
let log3_resolve_message = await logAsync('再 2 秒后会出现这句', 2000);
console.log(log3_resolve_message);
};
logMessage();
// 这个讯息过 1 秒才会出现
// 从 resolve 取得:这个讯息过 1 秒才会出现
// 这个讯息再过 1.5 秒才会出现
// 从 resolve 取得:这个讯息再过 1.5 秒才会出现
// 这个讯息再过 2 秒才会出现
// 从 resolve 取得:这个讯息再过 2 秒才会出现
使用 await Promise.all() 执行
await
在async function
里面才可以使用
(async () => {
let allLogMessage = await Promise.all([
logAsync('1 秒后会出现这句', 1000),
logAsync('再 1.5 秒后会出现这句', 1500),
logAsync('再 2 秒后会出现这句', 2000)
]);
console.log(allLogMessage);
})();
// 1 秒后会出现这句
// 再 1.5 秒后会出现这句
// 再 2 秒后会出现这句
// [
// '从 resolve 取得:1 秒后会出现这句',
// '从 resolve 取得:再 1.5 秒后会出现这句',
// '从 resolve 取得:再 2 秒后会出现这句'
// ]
全部在 Promise
中 resolve 的讯息,会用阵列的方式传给 await
前面的变数
使用 catch 抓取 reject() 传的错误讯息
在第 2 次呼叫 1.5 秒时,故意没有传秒数,导致程式发生错误呼叫 reject
logAsync('这个讯息过 1 秒才会出现', 1000)
.then((log1_resolve_message) => {
console.log(log1_resolve_message);
// 在这里故意没有传秒数,导致程式发生错误呼叫 reject
return logAsync('这个讯息再过 1.5 秒才会出现');
})
.then((log2_resolve_message) => {
console.log(log2_resolve_message);
return logAsync('这个讯息再过 2 秒才会出现', 2000);
})
.then((log3_resolve_message) => {
console.log(log3_resolve_message);
})
.catch((error) => {
console.log(error);
});
// 这个讯息过 1 秒才会出现
// 从 resolve 取得:这个讯息过 1 秒才会出现
// 无法处理 reject: 这个讯息再过 1.5 秒才会出现 & undefined
这里会发现 第 1 秒
有处理,但 1.5 秒
时就发生错误,后续的 2 秒
就没执行了
使用 Promise.all() catch 抓取 reject() 传的错误讯息
(async () => {
let allLogMessage = await Promise.all([
logAsync('1 秒后会出现这句', 1000),
logAsync('再 1.5 秒后会出现这句'),
logAsync('再 2 秒后会出现这句', 2000)
]);
console.log('----');
console.log(allLogMessage);
console.log('----');
})().catch((error) => {
console.log(error);
});
// 无法处理 reject: 再 1.5 秒后会出现这句 & undefined
// 1 秒后会出现这句
// 再 2 秒后会出现这句
在这里发现,再 1.5
秒的时候直接 catch 到错误,直接显示错误讯息
而 1 秒
与 2 秒
的部分皆有执行,所以表示所有的工作都是异步执行的
只是因为有任一个发生错误,所以 allLogMessage
就没有取得讯息,直接没有执行
将 Async 包成一个变数
const logMesssageAsync = async () => {
let allLogMessage = await Promise.all([
logAsync('1 秒后会出现这句', 1000),
logAsync('再 2 秒后会出现这句'),
logAsync('再 2 秒后会出现这句', 2000)
]);
console.log('----');
console.log(allLogMessage);
console.log('----');
};
let getLogMessageAsync = logMesssageAsync()
.then((success_message) => {
console.log(success_message)
}).catch((error) => {
console.log(error);
});
console.log(getLogMessageAsync);
// Promise { <pending> }
// 无法处理 reject: 再 2 秒后会出现这句 & undefined
// 1 秒后会出现这句
// 再 2 秒后会出现这句
因为 logMesssageAsync
被告之为 async
函式,所以在印出执行后的结果会拿到一个 Promise
物件 Promise { <pending> }
,就可以把这个变数当作 Promise
方式去操作
所以也可以写成这样,会得到一样的讯息
let getLogMessageAsync = logMesssageAsync();
console.log(getLogMessageAsync);
getLogMessageAsync
.then((success_message) => {
console.log(success_message)
}).catch((error) => {
console.log(error);
});
// Promise { <pending> }
// 无法处理 reject: 再 2 秒后会出现这句 & undefined
// 1 秒后会出现这句
// 再 2 秒后会出现这句
Promise 链
一直不断使用 then()
可以不断处理上一个 then()
处理的资料
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
console.log(result); // 1
return result * 2;
}).then(function(result) { // (***)
console.log(result); // 2
return result * 2;
}).then(function(result) {
console.log(result); // 4
return result * 2;
});
在迴圈使用 async / await
1. 使用 for await…of,每个 Promise 结束后可以马上取得结果
const sleep = (miniSecond) => {
return new Promise((resolve, reject) => {
console.log(`[sleep 秒数 - ${miniSecond}] enter`);
setTimeout(() => {
console.log(`[sleep 秒数 - ${miniSecond}] resolve`);
resolve(`秒数 ${miniSecond} 处理结果`);
}, miniSecond);
});
};
// 所有的 Promise 会在此时就发动
const SleepPromises = [sleep(1000), sleep(5000), sleep(2000)];
// 方法一:使用 for await,每个 Promise 结束后可以马上取得结果
(async function () {
for await (const item of SleepPromises) {
console.log('[for await 结果] ', item);
}
})();
// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [for await 结果] 秒数 1000 处理结果
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [for await 结果] 秒数 5000 处理结果
// [for await 结果] 秒数 2000 处理结果
依序执行的顺序是 1000
、5000
、2000
毫秒
上面可以看到,秒数 2000
的已经优先 resolve 了,但是 for await
在等第 2 个 5000
的结果处理完,才去处理 2000
的结果
2. 使用 for…of,每个 Promise 结束后可以马上取得结果
// 使用 for...of,每个 Promise 结束后可以马上取得结果
(async function () {
for (const item of SleepPromises) {
// 等待处理结果
const result = await item;
console.log('[for of 结果] ', result);
}
})();
// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [for of 结果] 秒数 1000 处理结果
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [for of 结果] 秒数 5000 处理结果
// [for of 结果] 秒数 2000 处理结果
将 await
写在 for 迴圈中,一样会依序等待处理结果
3. 使用 for 迴圈,每个 Promise 结束后可以马上取得结果
// 使用 for 迴圈,每个 Promise 结束后可以马上取得结果
(async function () {
for (let i = 0; i < SleepPromises.length; i++) {
// 等待处理结果
const result = await SleepPromises[i];
console.log('[for] 结果', result);
}
})();
// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [for] 结果 秒数 1000 处理结果
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [for] 结果 秒数 5000 处理结果
// [for] 结果 秒数 2000 处理结果
4. 使用 Promise.all,一次取得所有结果,需等待所有 Promise resolve 或其中一个被 reject 时终止
// 使用 Promise.all,一次取得所有结果,需等待所有 Promise resolve 或其中一个被 reject 时终止
(async function () {
const results = await Promise.all(SleepPromises);
console.log('[Promise.all] 结果', results);
})();
// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [Promise.all] 结果 [ '秒数 1000 处理结果', '秒数 5000 处理结果', '秒数 2000 处理结果' ]
5. 使用 Promise.allSettled,一次取得所有结果,需等待所有 Promise 都 resolve(fulfilled) / reject(rejected) 后终止
// 使用 Promise.allSettled,一次取得所有结果,需等待所有 Promise 都 resolve(fulfilled) / reject(rejected) 后终止
(async function () {
const results = await Promise.allSettled(SleepPromises);
console.log('[Promise.allSettled] 结果', results);
})();
// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [Promise.allSettled] 结果 [
// { status: 'fulfilled', value: '秒数 1000 处理结果' },
// { status: 'fulfilled', value: '秒数 5000 处理结果' },
// { status: 'fulfilled', value: '秒数 2000 处理结果' }
// ]
6. 使用 for…of await,一次取得所有结果,需等待所有 Promise resolve 或其中一个被 reject 时终止
不如就用 Promise.all 吧
// 使用 for...of await,一次取得所有结果,需等待所有 Promise resolve 或其中一个被 reject 时终止 吧)
(async function () {
for (const result of await Promise.all(SleepPromises)) {
console.log('[for...of await] 结果', result);
}
})();
// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [for...of await] 结果 秒数 1000 处理结果
// [for...of await] 结果 秒数 5000 处理结果
// [for...of await] 结果 秒数 2000 处理结果
参考资料
- Promises, async/await
- Promises chaining
- Async/await
- 我要学会 JS(三):callback、Promise 和 async/await 那些事儿
- Callback Hell
- 铁人赛:JavaScript Await 与 Async | 卡斯伯 Blog - 前端,没有极限
- 告别 JavaScript 的 Promise!迎接 Async/Await 的到来 · JIGSAWYE
- [JS] Async and Await in JavaScript | PJCHENder 未整理笔记
- await - JavaScript | MDN
- 简单理解 JavaScript Async 和 Await - OXXO.STUDIO
Donate KJ 贊助作者喝咖啡
如果這篇文章對你有幫助的話,可以透過下面支付方式贊助作者喝咖啡,如果有什麼建議或想說的話可以贊助並留言給我
If this article has been helpful to you, you can support the author by treating them to a coffee through the payment options below. If you have any suggestions or comments, feel free to sponsor and leave a message for me!
方式 Method | 贊助 Donate |
PayPal | https://paypal.me/kejyun |
綠界 ECPay | https://p.ecpay.com.tw/AC218F1 |
歐付寶 OPay | https://payment.opay.tw/Broadcaster/Donate/BD2BD896029F2155041C8C8FAED3A6F8 |