Deferred Navigation (การนำทางแบบรอผล) — คืออะไร หลักการและการนำไปใช้

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-06-10 เวลาอ่าน: 9 นาที

Deferred Navigation (การนำทางแบบรอผล) เป็นรูปแบบการนำทางแบบหน่วงเวลาที่การเปลี่ยนไปยังหน้าจอถัดไปเกิดขึ้นหลังจากดำเนินการแบบอะซิงโครนัสเสร็จสมบูรณ์ แทนที่จะเกิดขึ้นทันทีในขณะที่ผู้ใช้ดำเนินการ ตามข้อมูลจาก Android Developers (2024) การนำทางแบบรอผลช่วยหลีกเลี่ยงสภาวะการแข่งขันระหว่างการนำทางและการโหลดข้อมูล และช่วยให้การจัดการการเปลี่ยนจาก Push Notification และ Deeplink ง่ายขึ้น ความแตกต่างหลัก — เส้นทางจะถูกคำนวณหลังจากข้อมูลที่จำเป็นทั้งหมดพร้อมใช้งาน

ประเด็นสำคัญ

  • Deferred Navigation — การนำทางแบบหน่วงเวลาที่การเปลี่ยนเกิดขึ้นหลังจากดำเนินการแบบอะซิงโครนัสเสร็จ
  • การนำทางโดยตรง จัดการการเปลี่ยนทันที การนำทางแบบรอผลรอข้อมูล
  • สถานการณ์ทั่วไป: การยืนยันตัวตน, Deeplink, การแจ้งเตือนแบบ Push, การโหลดการกำหนดค่า
  • ใน Android นำไปใช้ผ่าน Navigation Component พร้อม Callback และ StateFlow
  • ใน iOS ใช้ Combine หรือ async/await กับตัวประสานงานการนำทาง

Deferred Navigation คืออะไร

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

ในทางสถาปัตยกรรม Deferred Navigation สร้างขึ้นบน การเปลี่ยนสถานะ: การกดปุ่มเริ่มกระบวนการแบบอะซิงโครนัส และการติดตามผลลัพธ์ของมันจะกระตุ้นการนำทาง สิ่งนี้สำคัญโดยเฉพาะในแอปพลิเคชันที่มีสถาปัตยกรรม MVVM หรือ MVI ซึ่ง ViewModel จัดการสถานะ และ View (Activity, Fragment, SwiftUI View) ติดตามการเปลี่ยนแปลงและตอบสนองด้วยการเปลี่ยน วิธีการนี้กำจัดการพึ่งพาโดยตรงระหว่าง UI และตรรกะการนำทาง

ตามข้อมูลจาก Google I/O 2023 การนำทางแบบรอผลได้รับการแนะนำสำหรับทุกสถานการณ์ที่การนำทางขึ้นอยู่กับผลลัพธ์ของคำขอเครือข่าย การตรวจสอบการยืนยันตัวตน การโหลดการกำหนดค่า หรือสิทธิ์ รูปแบบนี้ยังจำเป็นเมื่อจัดการกับ Deeplink ซึ่งแอปพลิเคชันต้องเริ่มทำงานก่อน โหลดหน้าจอราก และหลังจากนั้นจึงนำทางไปยังเส้นทางเป้าหมาย

เมื่อใดที่ต้องใช้การนำทางแบบรอผล

Deferred Navigation ใช้ในสถานการณ์ที่การนำทางโดยตรงนำไปสู่สถานะหน้าจอที่ไม่ถูกต้องหรือข้อผิดพลาดในการโหลด มาดูสี่กรณีหลักที่จำเป็นต้องใช้การนำทางแบบรอผล

การอนุญาตและการยืนยันตัวตน

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

การจัดการ Deeplink

เมื่อแอปพลิเคชันถูกเปิดผ่าน ลิงก์ภายนอก ต้องโหลดหน้าจอรากก่อน กู้คืนสถานะการนำทาง และหลังจากนั้นจึงดำเนินการเปลี่ยน Deeplink การนำทางโดยตรงไปยังหน้าจอเป้าหมายโดยไม่มีบริบทรากจะนำไปสู่ความผิดปกติ: สแต็กการนำทางว่างเปล่าหรือสแต็กย้อนกลับเสียหาย

การแจ้งเตือน Push พร้อมเนื้อหา

เมื่อแตะ การแจ้งเตือน Push แอปพลิเคชันสามารถอยู่ในหนึ่งในสามสถานะ: ปิด, อยู่ในพื้นหลัง หรือทำงานอยู่ Deferred Navigation กำหนดสถานะของแอปพลิเคชัน โหลดเนื้อหาที่จำเป็น และหลังจากนั้นจึงแสดงหน้าจอเป้าหมาย iOS อนุญาตให้จัดการสถานการณ์นี้ผ่าน UNNotificationContentExtension

ธงคุณลักษณะแบบไดนามิก

หากฟังก์ชันการทำงานของหน้าจอถูกควบคุมโดย ธงคุณลักษณะ จากเซิร์ฟเวอร์ การนำทางแบบรอผลช่วยให้สามารถขอการกำหนดค่าก่อน และหลังจากนั้นจึงแสดงหน้าจอ หากคุณลักษณะถูกปิดใช้งาน — ผู้ใช้เห็นเนื้อหาทางเลือกหรือตัวยึดแทนที่จะเป็นหน้าจอว่างเปล่า

สถานการณ์การนำทางโดยตรงDeferred Navigation
การอนุญาตหน้าจอว่างเมื่อโทเค็นหมดอายุเปลี่ยนเส้นทางไปที่เข้าสู่ระบบ
Deeplinkสแต็กย้อนกลับเสียหายสแต็กการนำทางที่ถูกต้อง
Pushการโหลดโดยไม่มีบริบทข้อมูลพร้อมก่อนการเปลี่ยน
ธงคุณลักษณะแสดงฟังก์ชันที่ไม่พร้อมใช้งานตัวยึดหรือทางเลือก

Deferred Navigation vs การนำทางโดยตรง

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

Deferred Navigation เพิ่มชั้นในรูปแบบของสถานะแบบอะซิงโครนัส เหตุการณ์ผู้ใช้เริ่มดำเนินการ และการติดตามผลลัพธ์ควบคุมการนำทาง สิ่งนี้เพิ่มความซับซ้อนของโค้ด แต่ให้ความยืดหยุ่น: ตัวกระตุ้นเดียวกันสามารถนำไปยังหน้าจอที่แตกต่างกันขึ้นอยู่กับข้อมูลที่โหลด

การเลือกระหว่าง สองวิธี ขึ้นอยู่กับข้อกำหนด: หากการแสดงหน้าจอไม่ต้องการข้อมูลแบบอะซิงโครนัส — ใช้การนำทางโดยตรง หากหน้าจอขึ้นอยู่กับผลลัพธ์ของคำขอ การอนุญาต หรือเงื่อนไขภายนอก — การนำทางแบบรอผลจำเป็น วิธีการแบบผสม ซึ่งการเปลี่ยนบางส่วนโดยตรงและบางส่วนหน่วงเวลา เป็นแนวทางปฏิบัติที่พบบ่อยที่สุดในแอปพลิเคชันอุตสาหกรรม

การนำไปใช้ใน Android: Navigation Component และ ViewModel

Android Jetpack มีกลไกสำหรับการนำ Deferred Navigation ไปใช้ในระดับสถาปัตยกรรม แนวคิดหลัก — ViewModel จัดการสถานะ ในขณะที่ Activity หรือ Fragment ติดตามการเปลี่ยนแปลงและกระตุ้นการนำทางผ่าน NavController

Deferred Navigation ด้วย StateFlow

StateFlow ใน Coroutine ของ Kotlin เป็นเครื่องมือที่เหมาะสำหรับการนำทางแบบรอผล ViewModel อัปเดต StateFlow ด้วยเหตุการณ์การนำทาง และ Activity สังเกตมันและดำเนินการเปลี่ยน เมื่อเหตุการณ์ถูกประมวลผล StateFlow จะถูกล้าง ป้องกันการนำทางซ้ำ

kotlin
class MainViewModel : ViewModel() {
    private val _navigation = MutableSharedFlow<NavigationEvent>()
    val navigation: SharedFlow<NavigationEvent> = _navigation

    fun onDeepLinkReceived(link: String) {
        viewModelScope.launch {
            val data = resolveDeepLink(link)
            _navigation.emit(NavigationEvent.GoToScreen(data))
        }
    }
}

Deferred Navigation ด้วย NavController

ใน Activity การติดตามการนำทางจะกระตุ้น NavController ด้วยเส้นทางจาก ViewModel เพื่อป้องกันการนำทางซ้ำเมื่อหมุนหน้าจอ ใช้ wrapper NavigationEventWrapper ที่ประมวลผลเหตุการณ์เพียงครั้งเดียว Jetpack Navigation 2.7+ รองรับ Safe Args สำหรับการส่งอาร์กิวเมนต์ที่ปลอดภัยตามชนิด

kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val vm: MainViewModel by viewModels()
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            vm.navigation.collect { event ->
                when (event) {
                    is NavigationEvent.GoToScreen ->
                        findNavController(R.id.nav_host)
                            .navigate(event.route)
                }
            }
        }
    }
}

การนำไปใช้ใน iOS: Coordinator Pattern และ Combine

iOS ไม่มี Navigation Component ในตัวที่คล้ายกับ Android Jetpack ดังนั้นนักพัฒนาจึงนำ Deferred Navigation ไปใช้ผ่าน Coordinator Pattern ร่วมกับ Combine หรือ async/await Coordinator จัดการสแต็กหน้าจอและตัดสินใจนำทางตามข้อมูลที่โหลด

Coordinator กับ Combine

Coordinator เป็นออบเจกต์ที่จัดการการนำทางระหว่าง ViewController เมื่อรวมกับ Combine ViewModel เผยแพร่เหตุการณ์ผ่าน PassthroughSubject และ Coordinator ติดตามเหตุการณ์เหล่านั้นและดำเนินการเปลี่ยน วิธีการนี้แยก UI ออกจากตรรกะการนำทางอย่างสมบูรณ์และเป็นไปตามคำแนะนำของ Apple เกี่ยวกับสถาปัตยกรรมแอปพลิเคชัน

swift
final class AppCoordinator {
    private var cancellables = Set<AnyCancellable>()

    func start(viewModel: MainViewModel) {
        viewModel.$navigationDestination
            .compactMap { $0 }
            .sink { [weak self] destination in
                self?.navigateTo(destination)
            }
            .store(in: &cancellables)
    }
}

Deferred Navigation ด้วย async/await

Swift 5.5 นำเสนอการทำงานพร้อมกันแบบมีโครงสร้าง ซึ่งอนุญาตให้นำการนำทางแบบรอผลไปใช้ผ่าน async/await โดยไม่ต้องใช้ Combine ViewModel ให้ฟังก์ชัน async ที่ส่งคืนเส้นทางหลังจากโหลดข้อมูล Coordinator เรียกฟังก์ชันนี้ใน Task และดำเนินการเปลี่ยนตามเส้นทางที่ได้รับ

swift
class AuthViewModel: ObservableObject {
    func resolveDeeplink(_ url: URL) async -> AppRoute? {
        guard let token = await AuthService.shared.getValidToken() else { return .login }
        return await DeeplinkRouter.resolve(url, token: token)
    }
}

// In Coordinator:
Task {
    if let route = await viewModel.resolveDeeplink(url) {
        navigateTo(route)
    }
}

ข้อผิดพลาดทั่วไปและแนวทางปฏิบัติที่ดีที่สุด

Deferred Navigation ทำให้การจัดการสถานการณ์แบบอะซิงโครนัสง่ายขึ้น แต่ต้องการวิธีการที่มีวินัยในการจัดการสถานะ มาดูข้อผิดพลาดหลักที่นักพัฒนาทำเมื่อนำการนำทางแบบรอผลไปใช้

ข้อผิดพลาด: การนำทางก่อนการเริ่มต้นเสร็จสมบูรณ์

ข้อผิดพลาดที่พบบ่อยที่สุด — การพยายามดำเนินการ การนำทางแบบรอผล ก่อนที่หน้าจอรากจะถูกเริ่มต้นอย่างสมบูรณ์และ NavController หรือ Coordinator พร้อมสำหรับการเปลี่ยน ใน Android สิ่งนี้นำไปสู่ IllegalStateException ใน iOS — นำไปสู่สถานะ UI ที่ไม่ได้กำหนด วิธีแก้ปัญหา — ตรวจสอบให้แน่ใจว่าวงจรชีวิตของคอมโพเนนต์อยู่ในสถานะ STARTED หรือ RESUMED ก่อนกระตุ้นการนำทาง

ข้อผิดพลาด: การนำทางซ้ำเมื่อกลับมาที่หน้าจอ

หาก StateFlow หรือ Subject ไม่ล้างเหตุการณ์หลังจากการประมวลผล เมื่อกลับมาที่หน้าจอก่อนหน้า ผู้ใช้อาจถูกเปลี่ยนเส้นทางไปยังหน้าจอเดียวกันโดยอัตโนมัติ ใช้ SharedFlow ที่มี replay=0 ใน Android หรือ CurrentValueSubject ที่มี nil หลังจากการประมวลผลใน iOS เพื่อให้เหตุการณ์การนำทางทำงานเพียงครั้งเดียว

แนวทางปฏิบัติที่ดีที่สุด: NavController หรือ Coordinator เดียว

ใช้ คอมโพเนนต์กลางหนึ่งตัว สำหรับการนำทางทั้งหมดในแอปพลิเคชัน เมื่อแต่ละ Activity, Fragment หรือ ViewController มีตัวควบคุมการนำทางของตัวเอง การนำทางแบบรอผลระหว่างส่วนต่าง ๆ ของแอปพลิเคชันจะวุ่นวาย Coordinator เดียวช่วยให้การดีบักและทดสอบสถานการณ์การนำทางง่ายขึ้น

แนวทางปฏิบัติที่ดีที่สุด: การทดสอบสถานการณ์การนำทางแบบรอผล

Deferred Navigation ทดสอบยากกว่าการนำทางโดยตรงเนื่องจากการดำเนินการแบบอะซิงโครนัสเพิ่ม ปัจจัยด้านเวลา ใช้ TestDispatcher ใน Android (kotlinx-coroutines-test) และ XCTestExpectation ใน iOS เพื่อจำลองการโหลดข้อมูลและตรวจสอบว่าการนำทางเป็นไปตามเส้นทางที่คาดหวัง จำลองบริการการอนุญาตและ Deeplink สำหรับการทดสอบแต่ละสถานการณ์อย่างแยกส่วน

คำถามที่พบบ่อย

Deferred Navigation แตกต่างจาก Deep Link อย่างไร

Deferred Navigation เป็นรูปแบบการเปลี่ยนแบบหน่วงเวลาที่สามารถนำไปใช้ในสถานการณ์แบบอะซิงโครนัสใดก็ได้ Deep Link เป็นตัวกระตุ้นหนึ่งสำหรับการนำทางแบบรอผล แต่ไม่ใช่เพียงตัวเดียว การยืนยันตัวตนและธงคุณลักษณะก็ใช้การนำทางแบบรอผลเช่นกัน

สามารถรวมการนำทางแบบรอผลและการนำทางโดยตรงได้หรือไม่

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

วิธีหลีกเลี่ยงสภาวะการแข่งขันในการนำทางแบบรอผล

ใช้ SharedFlow ที่ไม่มี replay ใน Android และ combineLatest ที่ไม่มีบัฟเฟอร์ใน iOS ยกเลิกการติดตามก่อนหน้าเมื่อมีตัวกระตุ้นใหม่ สิ่งนี้รับประกันว่าประมวลผลเฉพาะเหตุการณ์การนำทางล่าสุดเท่านั้น

Jetpack Compose รองรับการนำทางแบบรอผลหรือไม่

ได้ ใน Compose การนำทางแบบรอผลนำไปใช้ผ่านการติดตาม StateFlow ของ ViewModel และเรียก NavController.navigate ใน LaunchedEffect Google แนะนำให้ใช้ Navigation Compose ด้วยโมเดลที่ขับเคลื่อนด้วยเหตุการณ์สำหรับสถานการณ์แบบรอผล

วิธีจัดการการนำทางแบบรอผลเมื่อแอปพลิเคชันถูกย่อ

หน่วงเวลา การนำทางจนกว่าแอปพลิเคชันจะกลับมาทำงานอีกครั้ง ใน Android ใช้ Lifecycle.State.STARTED เพื่อกรองเหตุการณ์ ใน iOS ตรวจสอบ UIApplication.State ในบล็อก Combine หรือ async/await

สรุป

  • Deferred Navigation — รูปแบบที่การเปลี่ยนเกิดขึ้นหลังจากดำเนินการแบบอะซิงโครนัสเสร็จ
  • สถานการณ์หลัก: การยืนยันตัวตน, Deeplink, การแจ้งเตือน Push, ธงคุณลักษณะ
  • StateFlow/SharedFlow ใน Android และ Combine/async-await ใน iOS เป็นเครื่องมือในการนำไปใช้
  • Deferred Navigation ต่างจากการนำทางโดยตรงไม่ทำให้เกิดสภาวะการแข่งขันระหว่างการโหลดแบบอะซิงโครนัส
  • Navigation Component ใน Android และ Coordinator Pattern ใน iOS เป็นสถาปัตยกรรมพื้นฐาน
  • NavController/Coordinator ตัวเดียว จำเป็นสำหรับการนำทางที่สอดคล้องกัน
  • การทดสอบ การนำทางแบบรอผลต้องการ TestDispatcher และการจำลองสำหรับบริการแบบอะซิงโครนัส

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม