ใช้ a wait ภายนอกฟังก์ชัน async


86

ฉันพยายามเชื่อมโยงฟังก์ชัน async สองฟังก์ชันเข้าด้วยกันเนื่องจากฟังก์ชันแรกมีพารามิเตอร์ส่งคืนตามเงื่อนไขที่ทำให้วินาทีทำงานหรือออกจากโมดูล อย่างไรก็ตามฉันพบพฤติกรรมแปลก ๆ ที่ไม่พบในข้อกำหนด

async function isInLobby() {
    //promise.all([chained methods here])
    let exit = false;
    if (someCondition) exit = true;
}

นี่คือตัวอย่างโค้ดของฉันที่ถูกทำลาย (คุณสามารถดูขอบเขตทั้งหมดได้ที่นี่ ) ซึ่งจะตรวจสอบว่าผู้เล่นอยู่ในล็อบบี้แล้วหรือไม่ แต่ไม่เกี่ยวข้อง

ต่อไปเรามีฟังก์ชัน async นี้

async function countPlayer() {
    const keyLength = await scardAsync(game);
    return keyLength;
}

exit === trueฟังก์ชั่นนี้ไม่จำเป็นต้องให้ทำงานถ้า

ฉันพยายามที่จะทำ

const inLobby = await isInLobby();

สิ่งนี้ฉันหวังว่าจะรอผลลัพธ์ดังนั้นฉันจึงสามารถใช้inLobbyเพื่อเรียกใช้แบบมีเงื่อนไขได้countPlayerอย่างไรก็ตามฉันได้รับ typeerror โดยไม่มีรายละเอียดเฉพาะ

เหตุใดคุณจึงไม่สามารถawaitใช้asyncฟังก์ชันที่อยู่นอกขอบเขตของฟังก์ชันได้ ฉันรู้ว่ามันสัญญาน้ำตาลดังนั้นจึงต้องถูกล่ามโซ่thenแต่ทำไมมันที่อยู่ในcountPlayerที่ฉันจะรอสัญญาอื่น แต่ข้างนอกฉันไม่สามารถawait isInLobby?


คุณช่วยบอกเราได้ไหมว่าคุณทำที่ไหนawait isInLobby()และinLobbyใช้อย่างไร countPlayerเรียกว่าที่ไหน / อย่างไร
Bergi

@Bergi ฉันเชื่อมโยง repo ของฉันสำหรับบริบทจริง ใส่รหัสในคำถามมากเกินไป
Sterling Archer

ฉันไม่เห็นว่าปัญหาอยู่ตรงไหน (บางทีคุณอาจอัปเดต repo ไปแล้ว)? ถ้าคุณดูที่isInLobby().then( … countPlayer().then …ส่วนการแก้ปัญหาคือจิ๊บจ๊อย: เพียงแค่ทำให้ฟังก์ชั่นในการที่สายที่มีอยู่ (คนเดียว)(req, res) => async
Bergi

@Bergi ปัญหาไม่ได้อยู่ที่ว่ามันเสีย แต่ทำงานได้ตามที่เป็นอยู่ ฉันไม่เข้าใจว่าทำไมการรอคอยระดับบนสุดถึงไม่ได้เป็นอะไร ปรากฎว่ามันไม่มีอยู่จริงโดยไม่ได้กำหนดขอบเขตโมดูลทั้งหมดของคุณให้เป็นฟังก์ชัน async
Sterling Archer

แต่คุณไม่ต้องการระดับบนสุด awaitสำหรับโค้ดของคุณเลยหรือ? นั่นเป็นเหตุผลที่ฉันสงสัยว่าคุณยอมรับคำตอบที่ไม่เกี่ยวข้องกับปัญหาในคำถามจริงๆ
Bergi

คำตอบ:


74

awaitไม่รองรับระดับบนสุด มีการอภิปรายไม่กี่โดยคณะกรรมการมาตรฐานเกี่ยวกับเหตุผลนี้เป็นเช่นปัญหา Github นี้

นอกจากนี้ยังมีแง่คิดใน Githubเกี่ยวกับสาเหตุที่ระดับสูงสุดรอคอยจึงเป็นความคิดที่ไม่ดี โดยเฉพาะเขาแนะนำว่าหากคุณมีรหัสเช่นนี้:

// data.js
const data = await fetch( '/data.json' );
export default data;

ตอนนี้ไฟล์ใด ๆที่นำเข้าdata.jsจะไม่ดำเนินการจนกว่าการดึงข้อมูลจะเสร็จสมบูรณ์ดังนั้นการโหลดโมดูลทั้งหมดของคุณจึงถูกบล็อก ทำให้ยากมากที่จะให้เหตุผลเกี่ยวกับลำดับโมดูลของแอปเนื่องจากเราคุ้นเคยกับ Javascript ระดับบนสุดที่เรียกใช้งานพร้อมกันและคาดเดาได้ หากได้รับอนุญาตการรู้ว่าเมื่อใดที่ฟังก์ชันได้รับการกำหนดจะกลายเป็นเรื่องยุ่งยาก

มุมมองของฉันคือการปฏิบัติที่ไม่ดีสำหรับโมดูลของคุณที่จะมีผลข้างเคียงเพียงแค่โหลด นั่นหมายความว่าผู้บริโภคในโมดูลของคุณจะได้รับผลข้างเคียงเพียงแค่ต้องการโมดูลของคุณ สิ่งนี้ จำกัด ตำแหน่งที่สามารถใช้โมดูลของคุณได้ไม่ดี ระดับบนสุดawaitอาจหมายความว่าคุณกำลังอ่านจาก API บางตัวหรือเรียกใช้บริการบางอย่างในขณะโหลด แต่คุณควรส่งออกฟังก์ชัน async ที่ผู้บริโภคสามารถใช้ได้ด้วยตัวเอง


นั่นเป็นลิงค์ที่ดีขอบคุณ น่าเสียดายที่ไม่มีการสนับสนุนระดับบนสุด ฉันหวังว่ามันจะเป็น ในขณะนี้ฉันต้องทำตามคำสัญญาของฉันที่นี่และนั่นเป็นการปฏิบัติที่แย่มากและฉันไม่ชอบมัน :( ขอบคุณ
Sterling Archer

@SterlingArcher หรือใช้ async IIFE:void async function() { const inLobby = await isInLobby() }()
robertklep

@robertklep จะไม่กำหนดขอบเขตinLobbyของฟังก์ชันที่ทำให้ไม่สามารถเข้าถึงได้หรือไม่?
Sterling Archer

@SterlingArcher ใช่แล้วคุณจะต้องย้ายโค้ดทั้งหมดไปที่นั่น (โดยทั่วไปจะทำให้เป็น "ระดับบนสุด") เป็นเพียงทางเลือกในการใช้งาน.then()(ขออภัยควรทำให้ชัดเจนขึ้นอีกนิด)
robertklep

ไม่เห็นด้วยผู้ใช้ data.js ถูก 'บล็อก' อย่างไรก็ตามในขณะที่โหลด data.js เองและการอ้างอิงทั้งหมดแนวคิดของ 'การบล็อก' นี้ไม่ได้เลวร้ายในตัวมันเอง การรอระดับบนสุดอาจถือได้ว่าเป็นการโหลดการอ้างอิงบางอย่างซึ่งดูเหมือนว่าจะต้องมีก่อนการใช้งานจะถูก 'ปล่อย'
อิบราฮิมเบนซาลาห์

129

มีหลักสูตรนี้เสมอ:

(async () => {
    await ...

    // all of the script.... 

})();
// nothing else

สิ่งนี้ทำให้ฟังก์ชั่นด่วนด้วย async ที่คุณสามารถใช้ await ช่วยให้คุณไม่จำเป็นต้องสร้างฟังก์ชัน async ซึ่งยอดเยี่ยมมาก! // เครดิต Silve2611


3
โปรดอธิบายเพิ่มเติมและอธิบายว่าเหตุใดจึงได้ผล
พอย้อนกลับไป

12
มันโง่มากที่โหวตให้คำตอบนี้ มันทำให้ฟังก์ชั่นที่รวดเร็วด้วย async ที่คุณสามารถใช้ await ช่วยให้คุณไม่จำเป็นต้องสร้างฟังก์ชัน async ซึ่งยอดเยี่ยมมาก!
Silve2611

55
ไม่สามารถแก้ปัญหาได้เนื่องจากคุณยังต้องใช้awaitฟังก์ชันที่ไม่ระบุตัวตนนี้ซึ่งอีกครั้งจะไม่ทำงานจากภายนอกฟังก์ชัน
Michael

แล้วสิ่งนี้จะจัดการกับเงื่อนไขข้อผิดพลาดอย่างไร? มันลงจอดในรหัสรอด้วยหรือไม่?
Manuel Hernandez

นี่เป็นความช่วยเหลือที่ดีโดยเฉพาะอย่างยิ่งในการโทรกลับสัญญาเมื่อดึงข้อมูล API การเข้าสู่ระบบซึ่งตอบกลับอย่างสมบูรณ์แบบเพื่อรับพื้นที่เก็บข้อมูล getItem
tess hsu

10

ที่ดีไปกว่านั้นคือการใส่เครื่องหมายอัฒภาคเพิ่มเติมที่ด้านหน้าของบล็อกโค้ด

;(async () => {
    await ...
})();

ซึ่งจะป้องกันไม่ให้ฟอร์แมตเตอร์อัตโนมัติ (เช่นใน vscode) ย้ายพาเรนต์แรกไปที่ท้ายบรรทัดก่อนหน้า

ปัญหาสามารถแสดงได้จากตัวอย่างต่อไปนี้:

const add = x => y => x+y
const increment = add(1)
(async () => {
    await ...
})();

หากไม่มีเครื่องหมายอัฒภาคสิ่งนี้จะถูกจัดรูปแบบใหม่เป็น:

const add = x => y => x+y
const increment = add(1)(async () => {
  await Promise(1)
})()

ซึ่งเห็นได้ชัดว่าผิดเพราะกำหนดฟังก์ชัน async เป็นyพารามิเตอร์และพยายามเรียกใช้ฟังก์ชันจากผลลัพธ์ (ซึ่งเป็นสตริงแปลก ๆ'1async () => {...}')


20
ไม่ใช่คนปฏิรูปที่ผิด หากไม่มีเครื่องหมายอัฒภาคนั่นคือวิธีที่ฟังก์ชันของคุณจะถูกแยกวิเคราะห์ในรันไทม์และคุณจะได้รับข้อผิดพลาดตัวแบ่งบรรทัดหรือไม่มีตัวแบ่งบรรทัด วิธีแก้ปัญหาที่ถูกต้องในตัวอย่างของคุณคือการเพิ่มเครื่องหมายอัฒภาคหลังadd(1);และไม่อยู่หน้าฟังก์ชัน async
Madara's Ghost

11
นอกจากนี้ฉันไม่เข้าใจว่าสิ่งนี้เกี่ยวข้องกับคำถามในมืออย่างไร
Madara's Ghost

ใช่คุณพูดถูก. รันไทม์ยังต้องการเครื่องหมายอัฒภาค
Viliam Simko


1

คุณสามารถทำระดับบนสุดได้ตั้งแต่ typescript 3.8
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#-top-level-await
จากโพสต์:
เนื่องจากก่อนหน้านี้ใน JavaScript (พร้อมกับภาษาอื่น ๆ ส่วนใหญ่ที่มีคุณสมบัติคล้ายกัน) การรอคอยได้รับอนุญาตให้อยู่ในเนื้อความของฟังก์ชัน async เท่านั้น อย่างไรก็ตามด้วยการรอระดับบนสุดเราสามารถใช้การรอคอยที่ระดับบนสุดของโมดูล

const response = await fetch("...");
const greeting = await response.text();
console.log(greeting);

// Make sure we're a module
export {};

โปรดทราบว่ามีความละเอียดอ่อน: การรอระดับบนสุดจะทำงานที่ระดับบนสุดของโมดูลเท่านั้นและไฟล์จะถือว่าเป็นโมดูลเมื่อ TypeScript พบการนำเข้าหรือส่งออกเท่านั้น ในบางกรณีพื้นฐานคุณอาจต้องเขียนการส่งออก {} เป็นเอกสารสำเร็จรูปเพื่อให้แน่ใจว่าสิ่งนี้

การรอระดับบนสุดอาจไม่ทำงานในทุกสภาพแวดล้อมที่คุณอาจคาดหวัง ณ จุดนี้ ขณะนี้คุณสามารถใช้ระดับบนสุดรอได้ก็ต่อเมื่ออ็อพชันคอมไพเลอร์เป้าหมายคือ es2017 ขึ้นไปและโมดูลเป็น esnext หรือ system การสนับสนุนภายในหลายสภาพแวดล้อมและบันเดิลเลอร์อาจถูก จำกัด หรืออาจต้องเปิดใช้งานการสนับสนุนทดลอง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.