ฟังก์ชันอะซิงโครนัสช่วยให้คุณเขียนโค้ดตามสัญญาได้เสมือนกับว่าเป็นแบบซิงโครนัส
ระบบจะเปิดใช้ฟังก์ชัน Async โดยค่าเริ่มต้นใน Chrome, Edge, Firefox และ Safari และ ก็เป็นเรื่องที่มหัศจรรย์มากทีเดียว ที่ให้คุณเขียนโค้ดตามคำสัญญา หากเป็นแบบซิงโครนัสแต่ไม่บล็อกเทรดหลัก องค์ประกอบเหล่านี้ทำให้ โค้ดแบบอะซิงโครนัส "ฉลาด" น้อยลง และอ่านง่ายขึ้น
ฟังก์ชันอะซิงโครนัสมีการทำงานดังนี้
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await promise;
} catch (rejectedValue) {
// …
}
}
หากใช้คีย์เวิร์ด async
ก่อนคําจํากัดความของฟังก์ชัน คุณจะใช้
await
ภายในฟังก์ชัน เมื่อคุณawait
ให้คำมั่นสัญญา ฟังก์ชันจะหยุดชั่วคราว
ด้วยวิธีที่ไม่บล็อกจนกว่าสัญญาจะตกลง หากคำมั่นสัญญาจะทำให้
เพื่อให้คุณได้รับมูลค่าคืน หากคำสัญญาปฏิเสธ ระบบจะส่งค่าที่ถูกปฏิเสธ
การสนับสนุนเบราว์เซอร์
ตัวอย่าง: การบันทึกการดึงข้อมูล
สมมติว่าคุณต้องการดึงข้อมูล URL และบันทึกคำตอบเป็นข้อความ ลักษณะของป้ายเป็นแบบนี้ โดยใช้คำสัญญา:
function logFetch(url) {
return fetch(url)
.then((response) => response.text())
.then((text) => {
console.log(text);
})
.catch((err) => {
console.error('fetch failed', err);
});
}
และนี่คือสิ่งเดียวกันนี้เมื่อใช้ฟังก์ชันอะซิงโครนัส:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
} catch (err) {
console.log('fetch failed', err);
}
}
จำนวนบรรทัดเท่ากัน แต่ Callback ทั้งหมดจะหายไป เข้าใจแล้ว อ่านง่ายขึ้น โดยเฉพาะสำหรับผู้ที่ไม่ค่อยคุ้นเคยกับสัญญา
ค่าการแสดงผลแบบไม่พร้อมกัน
ฟังก์ชันอะซิงโครนัสจะให้ผลลัพธ์ทุกครั้ง ไม่ว่าคุณจะใช้ await
หรือไม่ก็ตาม นั่น
จะแปลงผลลัพธ์โดยที่ฟังก์ชันอะซิงโครนัสส่งคืน หรือปฏิเสธ
อะไรก็ตามที่ฟังก์ชันอะซิงโครนัสอัปโหลด ดังนั้นด้วย:
// wait ms milliseconds
function wait(ms) {
return new Promise((r) => setTimeout(r, ms));
}
async function hello() {
await wait(500);
return 'world';
}
...การเรียก hello()
จะแสดงคำสัญญาว่าจะตอบสนองด้วย "world"
async function foo() {
await wait(500);
throw Error('bar');
}
...การเรียก foo()
จะแสดงการสัญญาว่าปฏิเสธ Error('bar')
ตัวอย่าง: การสตรีมคำตอบ
ประโยชน์ของฟังก์ชันแบบไม่พร้อมกันจะเพิ่มขึ้นเมื่อมีตัวอย่างที่ซับซ้อนมากขึ้น พูดว่าอยากได้ เพื่อสตรีมคำตอบขณะตัดกลุ่มบางส่วนออก และแสดงผลขนาดสุดท้าย
ต่อไปนี้คือสิ่งที่สัญญาไว้
function getResponseSize(url) {
return fetch(url).then((response) => {
const reader = response.body.getReader();
let total = 0;
return reader.read().then(function processResult(result) {
if (result.done) return total;
const value = result.value;
total += value.length;
console.log('Received chunk', value);
return reader.read().then(processResult);
});
});
}
ดูสิ Jake "ผู้นำทางแห่งคำสัญญา" ที่เก็บ ดูว่าฉันโทรมาอย่างไร
processResult()
ภายในตัวเองเพื่อตั้งค่าการวนซ้ำแบบไม่พร้อมกันใช่ไหม งานเขียนที่สร้าง
ฉันรู้สึกฉลาดมากเลย แต่ชอบที่ "ฉลาด" ส่วนใหญ่ คุณจะต้องจับตาดูโค้ดนั้น
เพื่อค้นหาสิ่งที่กำลังทำอยู่ เช่น รูปภาพดวงตาวิเศษจาก
ยุค 90
โปรดลองอีกครั้งด้วยฟังก์ชันอะซิงโครนัส
async function getResponseSize(url) {
const response = await fetch(url);
const reader = response.body.getReader();
let result = await reader.read();
let total = 0;
while (!result.done) {
const value = result.value;
total += value.length;
console.log('Received chunk', value);
// get the next result
result = await reader.read();
}
return total;
}
"ฉลาด" ทุกคน หายไปแล้ว การวนซ้ำแบบไม่พร้อมกันที่ทำให้ฉันรู้สึกมั่นใจมากคือ
แทนที่ด้วยรายการที่เชื่อถือ น่าเบื่อ และลูป ยิ่งไปกว่านั้น ในอนาคต คุณจะได้รับ
ตัวทำซ้ำแบบไม่พร้อมกัน
ซึ่งจะ
แทนที่ลูป while
ด้วยวงแหวนสำหรับเชื่อมต่อ ซึ่งทำให้ดูเรียบร้อยยิ่งขึ้น
ไวยากรณ์ฟังก์ชันแบบไม่พร้อมกันอื่นๆ
เราแสดง async function() {}
ให้คุณดูแล้ว แต่คีย์เวิร์ด async
สามารถเป็น
ที่ใช้กับไวยากรณ์ของฟังก์ชันอื่น
ฟังก์ชันลูกศร
// map some URLs to json-promises
const jsonPromises = urls.map(async (url) => {
const response = await fetch(url);
return response.json();
});
เมธอดของออบเจ็กต์
const storage = {
async getAvatar(name) {
const cache = await caches.open('avatars');
return cache.match(`/avatars/${name}.jpg`);
}
};
storage.getAvatar('jaffathecake').then(…);
วิธีการสำหรับชั้นเรียน
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jaffathecake').then(…);
ระวังด้วยนะ หลีกเลี่ยงการเรียงลำดับมากเกินไป
แม้ว่าคุณจะกำลังเขียนโค้ดที่ดูพร้อมกัน แต่โปรดไม่พลาด ในการทำสิ่งต่างๆ ไปพร้อมๆ กัน
async function series() {
await wait(500); // Wait 500ms…
await wait(500); // …then wait another 500ms.
return 'done!';
}
ข้อมูลข้างต้นใช้เวลา 1, 000 มิลลิวินาทีจึงจะเสร็จสมบูรณ์ ในขณะที่
async function parallel() {
const wait1 = wait(500); // Start a 500ms timer asynchronously…
const wait2 = wait(500); // …meaning this timer happens in parallel.
await Promise.all([wait1, wait2]); // Wait for both timers in parallel.
return 'done!';
}
วิธีการข้างต้นใช้เวลา 500 มิลลิวินาทีในการดำเนินการ เนื่องจากการรอทั้งสองแบบเกิดขึ้นพร้อมกัน มาดูตัวอย่างที่ใช้ได้จริงกัน
ตัวอย่าง: การแสดงเอาต์พุตตามลำดับ
สมมติว่าคุณต้องการดึงข้อมูลชุด URL และบันทึกไว้โดยเร็วที่สุดใน ลำดับที่ถูกต้อง
หายใจลึกๆ - ตัวอย่างเส้นทางมีดังนี้
function markHandled(promise) {
promise.catch(() => {});
return promise;
}
function logInOrder(urls) {
// fetch all the URLs
const textPromises = urls.map((url) => {
return markHandled(fetch(url).then((response) => response.text()));
});
// log them in order
return textPromises.reduce((chain, textPromise) => {
return chain.then(() => textPromise).then((text) => console.log(text));
}, Promise.resolve());
}
ถูกต้อง ฉันใช้ reduce
เพื่อผูกการเรียงลำดับคำสัญญา ฉัน คือ
อัจฉริยะ แต่วิธีนี้เป็นการเขียนโค้ดที่ฉลาดมากสักหน่อย คุณจะมีประสิทธิภาพมากกว่าหากไม่มี
อย่างไรก็ตาม เมื่อแปลงฟังก์ชันข้างต้นเป็นฟังก์ชันอะซิงโครนัส คุณอาจอยากใช้งาน ตามลำดับมากเกินไป
async function logInOrder(urls) { for (const url of urls) { const response = await fetch(url); console.log(await response.text()); } }
function markHandled(...promises) { Promise.allSettled(promises); } async function logInOrder(urls) { // fetch all the URLs in parallel const textPromises = urls.map(async (url) => { const response = await fetch(url); return response.text(); }); markHandled(...textPromises); // log them in sequence for (const textPromise of textPromises) { console.log(await textPromise); } }
วิธีแก้ปัญหาการสนับสนุนเบราว์เซอร์: โปรแกรมสร้าง
หากคุณกำหนดเป้าหมายเป็นเบราว์เซอร์ที่รองรับโปรแกรมสร้าง (ซึ่งรวมถึง เวอร์ชันล่าสุดของเบราว์เซอร์หลักๆ ทั้งหมด ) คุณสามารถจัดเรียงฟังก์ชันอะซิงโครนัส Polyfill ได้
Babel จะดำเนินการดังกล่าวให้คุณ นี่คือตัวอย่างจากการตอบกลับ Babel
- ดูว่าโค้ดที่แปลงแล้วมีความคล้ายคลึงกันเพียงใด การเปลี่ยนรูปแบบนี้เป็นส่วนหนึ่งของ ค่าที่กำหนดล่วงหน้าของ es2017 ของ Babel
ผมขอแนะนำวิธีการเปลี่ยนผ่าน เพราะคุณจะปิดได้เมื่อ เบราว์เซอร์เป้าหมายสนับสนุนฟังก์ชันอะซิงโครนัส แต่ถ้าคุณไม่ต้องการใช้ Transpiler คุณสามารถดำเนินการ โพลีฟิลล์ของ Babel และใช้งานด้วยตัวคุณเอง แทนที่จะเป็น:
async function slowEcho(val) {
await wait(1000);
return val;
}
...คุณจะต้องใส่ polyfill และเขียนว่า
const slowEcho = createAsyncFunction(function* (val) {
yield wait(1000);
return val;
});
โปรดทราบว่าคุณต้องส่งโปรแกรมสร้าง (function*
) ไปยัง createAsyncFunction
และใช้ yield
แทน await
นอกจากนี้ ยังทำงานเหมือนเดิม
วิธีแก้ปัญหาเบื้องต้น: เครื่องมือสร้างใหม่
และหากคุณกำหนดเป้าหมายไปที่เบราว์เซอร์รุ่นเก่า Babel ก็สามารถแปลงเครื่องมือสร้าง คุณสามารถใช้ฟังก์ชันอะซิงโครนัสได้จนถึง IE8 หากต้องการดำเนินการนี้ คุณจำเป็นต้อง ค่าที่กำหนดล่วงหน้า es2017 ของ Babel และค่าที่กำหนดล่วงหน้า es2015
เอาต์พุตค่อนข้างไม่ดี ดังนั้นโปรดระวัง และการขยายโค้ด
ไม่ซิงค์ทุกเรื่องเลย!
เมื่อฟังก์ชันอะซิงโครนัสเข้ามาอยู่ในทุกเบราว์เซอร์แล้ว ให้ใช้ฟังก์ชันดังกล่าวในทุก มีฟังก์ชันที่ได้ผลแน่นอน โค้ดเหล่านี้ไม่เพียงแต่ทำให้โค้ดของคุณเป็นระเบียบขึ้น ฟังก์ชันนั้นจะทำให้ได้รับคำสัญญาเสมอ
ฉันตื่นเต้นมากกับฟังก์ชัน แบบไม่พร้อมกันกลับมา 2014 และ เป็นเรื่องดีที่ได้เห็นพวกเขาไปปรากฏตัวจริงบนเบราว์เซอร์ ไชโย!