การสร้างเว็บไซต์ที่ปรับแต่งหน้าตาได้พร้อมทั้งสามารถเพิ่มเติมชุด Web Parts ลงไปได้ ถือเป็นเรื่องที่ง่ายดายอย่างมาก ถ้าหากคุณใช้โครงสร้างพื้นฐานประตูท่าของ ASP.NET 2.0 โมเดลนี้จัดว่ามีความคล่องตัวอย่างมาก โดยยอมให้ผู้ใช้ใส่ Web Parts ลงไปตรงจุดใดก็ได้ในเว็บเพจ เพื่อที่พวกเขาจะสามารถปรับแต่งหน้าตาของเว็บไซต์ได้โดยอิสระ อย่างไรก็ตามปมเด่นต่างๆเหล่านี้อาจนำไปสู่ระบบที่ไร้ประสิทธิภาพจนทำให้ผู้ใช้ไม่พอใจได้ เนื่องจากคุณไม่มีทางทราบได้ก่อนว่าจะมีคอมโพเน้นต์ใดที่สามารถนำมาใช้ร่วมกันบ้าง ด้วยเหตุนี้คุณจึงไม่สามารถปรับแต่งให้การเรียกใช้ข้อมูลของคอมโพเน้นต์แต่ละชนิดมีประสิทธิภาพสูงสุดได้
ปัญหาความไร้ประสิทธิภาพที่มักพบในไซต์ประตูท่าทั่วไปเกิดขึ้นเมื่อมี Web Parts จำนวนมากเรียกขอข้อมูลจากระบบเครือข่ายพร้อมๆกัน คำสั่งแต่ละอัน (ไม่ว่าจะเป็นเว็บเซอร์วิสหรือรีโมทดาต้าเบสก็ตาม) มักจะทำให้การประมวลผลเพจโดยรวมเสียเวลานานขึ้น แม้ว่าคำสั่งเหล่านี้จะเป็นอิสระต่อกันและสามารถออกคำสั่งแบบคู่ขนานได้ก็ตาม
โชคดีที่ ASP.NET 2.0 มีโมเดลอะซิงโครนัสเพจที่ใช้ง่ายที่เมื่อใช้ร่วมกับการเรียกเว็บเซอร์วิสแบบอะซิงโครนัส และการเรียกใช้ดาต้าเบสแบบอะซิงโครนัสแล้วจะทำให้เวลาในการตอบสนองของเพจประตูท่าดีขึ้นอย่างมาก เนื่องจาก Web Parts อิสระจำนวนมากสามารถเรียกใช้ข้อมูลแบบคู่ขนานได้ ดังนั้นผมจะพูดถึงการสร้าง Web Parts ที่ทำการเรียกข้อมูลแยกมากขึ้น เพื่อทำให้เพจประตูท่าที่เก็บ Web Parts เหล่านี้เอาไว้ตอบสนองและขยายระบบได้ดีขึ้นกว่าเดิม
การใช้ Web Part
เรามาเริ่มต้นโดยการพิจารณาตัวอย่างเพจประตูท่าที่แสดงอยู่ในภาพที่ 1 กันก่อน ในตัวอย่างนี้เรามี Web Parts จำนวน 4 ชนิดอยู่ในเพจประตูท่า โดยแต่ละชนิดจะเรียกข้อมูลมาจากแหล่งข้อมูลที่แตกต่างกัน ซอร์ซโค้ดทั้งหมดของแอพพลิเคชันตัวอย่างนี้มีให้ดาวน์โหลดที่เว็บไซต์ของ MSDN Magazine และผมอยากให้คุณทบทวนแอพพลิเคชันดังกล่าวขณะที่คุณกำลังอ่านบทความนี้อยู่ ซึ่งในตัวอย่างนี้มี Web Parts สามอันเรียกข้อมูลมาจากเว็บเซอร์วิส โดยมีการรอ 3 วินาทีก่อนที่จะส่งผลลัพธ์กลับมา ส่วน Web Parts อันที่ 4 ส่งคิวรี ADO.NET ไปยังดาต้าเบส SQL Server ซึ่งก็มีการรอ 3 วินาทีเช่นกันก่อนที่จะส่งผลลัพธ์กลับมา แม้ปัญหานี้ดูออกจะเกินจริงอยู่บ้าง แต่ก็มีโอกาสเป็นไปได้
ภาพที่ 1 ตัวอย่างเพจประตูท่า
Web Parts แต่ละอันในแอพพลิเคชันตัวอย่างถูกสร้างขึ้นมาโดยใช้ยูซเซอร์คอนโทรลและเชื่อมโยงผลลัพธ์ของข้อมูลที่ได้รับมาไปยังคอนโทรลที่ใช้แสดงผล โค้ดและมาร์กอัพของคอนโทรลแต่ละอันพยายามทำให้มีจำนวนน้อยที่สุดเพื่อที่ตัวอย่างจะได้เรียบง่ายที่สุด จากนั้นคุณก็จะหันไปให้ความสำคัญกับการทำให้ Web Parts ทำงานแบบอะซิงโครนัสได้
นี่คือตัวอย่างไฟล์ยูซเซอร์คอนโทรล NewsWebPart.ascx
<%@ Control Language="C#"
AutoEventWireup="true"
CodeFile="NewsWebPart.ascx.cs"
Inherits="webparts_
NewsWebPart" %>
<asp:BulletedList ID="_newsHeadlines"
runat="server">
</asp:BulletedList>
public partial class webparts_NewsWebPart : UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
PortalServices ps = new PortalServices();
_newsHeadlines.DataSource = ps.GetNewsHeadlines();
_newsHeadlines.DataBind();
}
}
หมายเหตุ: ให้คุณลองสังเกตวิธีการที่ Web Parts โต้ตอบกับเว็บเซอร์วิสเพื่อเรียกดูข้อมูลพาดหัวข่าวตัวอย่างดู ในขณะที่ Web Part เรียกดูราคาหุ้นและ Web Part ข้อมูลพยากรณ์อากาศทำงานแบบเดียวกัน แต่ใช้ method ที่ต่างกันของเว็บเซอร์วิสเดียวกันเพื่อดึงข้อมูล สิ่งที่คล้ายคลึงกันก็คือภาพที่ 2 แสดงไฟล์ยูซเซอร์คอนโทรล SalesReportWebPart.ascx และไฟล์โค้ดเบื้องหลังที่เกี่ยวข้องเพื่อใช้กับ Web Part ตัวอย่างรายงานยอดขาย ให้คุณลองสังเกตวิธีการใช้คอนโทรลใช้ ADO.NET เพื่อดึงข้อมูลยอดขายมาจากดาต้าเบส แล้วนำเอาข้อมูลดังกล่าวไปใส่ไว้ในคอนโทรล GridView
ภาพที่ 3 การประมวลผล Web Part เชิงเส้น
เมื่อเพจประตูท่าตัวอย่างเริ่มทำงาน ปัญหาก็เริ่มเห็นได้อย่างชัดเจนมากขึ้น ระบบต้องใช้เวลามากกว่า 12 วินาทีเพื่อเรียกการประมวลผล ซึ่งเป็นความล่าช้าที่จะทำให้ผู้ใช้ส่วนใหญ่ไม่กลับมาใช้แอพพลิเคชันนี้อีก สาเหตุที่เกิดความล่าช้านานขนาดนี้เหมือนอย่างที่แสดงเอาไว้ในภาพที่ 3 มีสาเหตุมาจากเส้นทางที่ใช้ในการประมวลผลคำสั่งขณะที่เพจถูกสั่งงาน สิ่งที่เหมือนกับคอนโทรลอื่นๆในโครงสร้างแยกสาขาคอนโทรลในเพจก็คือ Web Part แต่ละอันจะถูกโหลดเรียงตามลำดับที่กำหนดโดยโครงสร้างแยกสาขาของคอนโทรลที่อยู่ในเพจ การที่การประมวลผลเป็นเชิงเส้น ดังนั้น Web Part แต่ละอันจึงจำเป็นต้องรอให้ Web Part อันก่อนหน้าในโครงสร้างทำงานให้เสร็จเสียก่อน จึงจะเริ่มต้นเรียกใช้ข้อมูลของตนเองและเตรียมตัวที่จะทำการตอบสนองได้ การที่ความล่าช้าปลอม 3 วินาทีเกิดขึ้นในช่วงของการเรียกใช้ข้อมูล ดังนั้นการตอบสนองทั้งหมดจึงต้องใช้เวลา 12 วินาที Web Part แต่ละอันทำการดึงข้อมูลที่ไม่เกี่ยวข้องกันโดยสิ้นเชิงทีละอันจนกว่าจะเสร็จ สิ่งสำคัญที่พึงตระหนักก็คือการเรียกข้อมูลทั้งหมดควรเกิดขึ้นแบบคู่ขนาน ก็จะทำให้เวลาในการตอบสนองลดลงร้อยละ 75 นั่นคือเป้าหมายที่ผมวางเอาไว้
การเรียกใช้เว็บแบบอะซิงโครนัส
ในตัวอย่างนี้ Web Parts สามอันใช้เว็บเซอร์วิสเพื่อดึงข้อมูลของตนเอง และ Web Part อีกอันหนึ่งใช้ ADO.NET เพื่อเรียกใช้ดาต้าเบส เราจะมาเริ่มต้นโดยการทำให้เว็บเซอร์วิสตัวนี้ทำงานแบบอะซิงโครนัสได้ เนื่องจากเครื่องมือ Web Services Description Language ที่ชื่อ WSDL.exe (หรือเครื่องมือ Visual Studio 2005 Add Web Service Reference) สามารถสร้างบริการชั้นดีในเว็บเซอร์วิสพร็อกซีคลาสขึ้นมาได้สำหรับการเรียก Web method แบบอะซิงโครนัสนี้ได้
เมื่อมีการสร้างเว็บเซอร์วิสพร็อกซีคลาสขึ้นมาใน ASP.NET 2.0 ที่จริงแล้วมีการสร้างวิธีการที่แตกต่างกันออกไป 3 แบบสำหรับเรียก method ใดๆขึ้นมา โดยจะเป็นการเรียกแบบซิงโครนัสหนึ่งวิธี และเป็นแบบอะซิงโครนัสสองวิธี ตัวอย่างเช่นเว็บเซอร์วิสพร็อกซีที่ Web Parts กำลังใช้อยู่มี method เหล่านี้ให้ใช้สำหรับเรียก GetNewsHeadlines Web method ได้
public string[] GetNewsHeadlines()
public IAsyncResult BeginGetNewsHeadlines(
AsyncCallback callback, object asyncState)
public string[] EndGetNewsHeadlines( IAsyncResult asyncResult)
public void GetNewsHeadlinesAsync()
public void GetNewsHeadlinesAsync( object userState)
public event
GetNewsHeadlinesCompletedEventHandler
GetNewsHeadlinesCompleted;
method ชนิดแรกที่ชื่อ GetNewsHeadlines จัดเป็น method ซิงโครนัสแบบมาตรฐาน ส่วน method อีกสองอันก็คือ BeginGetNewsHeadlines และ EndGetNewsHeadlines สามารถใช้เพื่อเรียก method แบบอะซิงโครนัสได้ และสามารถเชื่อมโยงเข้าหากลไกอะซิงโครนัสมากน้อยขนาดไหนก็ได้ใน .NET ผ่านทางอินเทอร์เฟซ IASyncResult แบบมาตรฐาน
แต่ method ที่น่าสนใจที่สุดตามแนวทางนี้ก็คือ method อันสุดท้ายที่ชื่อ GetNewsHeadlinesAsync การที่จะใช้ method นี้ได้ ผมต้องลงจองสร้าง delegate ตัวหนึ่งขึ้นมาพร้อมกับเหตุการณ์ของพร็อกซีคลาส ที่ถูกสร้างขึ้นมาเพื่อรวบรวมผลลัพธ์ของคำสั่งแบบอะซิงโครนัส (ในตัวอย่างนี้ก็คือ GetNewsHeadlinesCompleted event) จากนั้น delegate signature จะถูกกำหนดให้รับค่าที่คืนมาของ method เพื่อที่ผมจะได้ตัดต่อผลลัพธ์ใน method ไปใช้งาน
การใช้ method อะซิงโครนัสที่อิงกับเหตุการณ์นี้ ช่วยให้การแก้ไขคำสั่ง Web method ที่อยู่ใน Web Part ข่าวพาดหัวให้สามารถทำงานแบบอะซิงโครนัสเป็นเรื่องที่ทำได้ง่ายมากเหมือนอย่างที่แสดงเอาไว้ในภาพที่ 4 ก่อนอื่นผมทำการจองตัวแทนให้แก่ GetNewsHeadlinesCompleted event ของพร็อกซีคลาส ตามด้วยการเรียก GetNewsHeadlinesAsync method ส่วนวิธีการใช้ method ที่จองเอาไว้ให้แก่ GetNewsHeadlinesCompleted event ผมได้เชื่อมผลลัพธ์ของการเรียก Web method ไปยัง BulletedList เพื่อส่งการแสดงผลไปยังไคล์เอ็นต์ สิ่งที่จำเป็นต้องพิจารณาอีกอย่างหนึ่งก็คือ method อะซิงโครนัสเหล่านี้จะทำงานได้ก็ต่อเมื่อ Web Part ถูกใส่เอาไว้ในเพจโดยมีการกำหนดค่าของ Async ให้เป็น true เท่านั้น ซึ่งคุณสามารถตรวจสอบการเขียนโปรแกรมได้โดยเข้าไปดูที่ IsAsync property ของเพจที่เก็บ Web Parts เอาไว้ ถ้าหากเพจที่ใส่ Web Part เอาไว้ไม่ได้มีค่าเป็นอะซิงโครนัส ผมจึงต้องใช้การเชื่อมโยงอะซิงโครนัสแบบมาตรฐานแทนเหมือนอย่างที่แสดงเอาไว้ในภาพที่ 4
ในตอนนี้เพื่อทำให้ Web Part อะซิงโครนัสเรียกข้อมูลแบบอะซิงโครนัสได้ มันจะต้องใส่เอาไว้ในเพจที่ตัวแปร Async กำหนดให้เป็น true ดังนั้นผมจึงได้ทำการแก้ไข Page directive ของเพจประตูท่าให้มีหน้าตาแบบนี้
<%@ Page Language="C#" AutoEventWireup="true" Async="true" %>
หลังจากที่ผมทำการแก้ไข Web Parts อีกสองอันที่ใช้เว็บเซอร์วิสเพื่อดึงข้อมูลแบบอะซิงโครนัสแล้ว เพจประตูท่าก็จะตอบสนองได้ดีขึ้นกว่าเดิมมาก ที่จริงแล้วมันอาจแสดงผลไปยังไคล์เอ็นต์โดยใช้เวลาแค่ 3 วินาทีเท่านั้น ขึ้นอยู่กับลำดับการโหลด Web Parts (ถ้าหากมีการโหลด Web Parts รายงานยอดขายก่อน มันจะใช้เวลามากกว่า 6 วินาที) แม้ว่า Web Part รายงานยอดขายยังคงเรียกใช้ดาต้าเบสเชิงเส้นอยู่ก็ตาม แต่ในตอนนี้ Web Parts อื่นๆอีก 3 อันจะสั่งงานเว็บเซอร์วิสแบบอะซิงโครนัส ดังนั้นคำสั่งของการทำงานหลักจึงไม่จำเป็นต้องรอให้ Web Part อื่นๆทำงานเสร็จอีกต่อไป แน่ละผมต้องการให้งานทั้งหมดที่เกี่ยวข้องกับ I/O ทำงานแบบอะซิงโครนัสได้ เพื่อที่ไคล์เอ็นต์จะมีโอกาสใช้ทั้งเว็บเซอร์วิสและ Web Part ที่อิงกับดาต้าเบสได้โดยที่ไม่ต้องรออีกต่อไป
เหตุผลอื่นๆที่จะดัดแปลงให้งานที่เกี่ยวข้องกับ I/O กลายเป็นการเรียกใช้ I/O แบบอะซิงโครนัสก็คือการช่วยให้การทำงานหลักกลับมาอยู่ในกลุ่มการทำงานเร็วขึ้นเพื่อช่วยให้สามารถให้บริการคำสั่งอื่นๆได้ ถ้าหากตอนนี้ผมดึงงานหลักกลับมาก็ต่อเมื่อการคิวรีดาต้าเบสรายงานยอดขายเสร็จแล้วเท่านั้น นั่นหมายความว่าผมต้องรอเฉยๆ 3 วินาทีเต็มๆ ในขณะที่กลุ่มการทำงานน่าจะถูกใช้ไปให้บริการคำสั่งอื่นๆได้ ดังนั้นถ้าหากผมสามารถทำให้คำสั่งที่เกี่ยวกับ I/O อันสุดท้ายนี้เรียกใช้ข้อมูลแบบอะซิงโครนัสได้เช่นกัน เพจของผมก็จะใช้คำสั่งดังกล่าวโดยใช้เวลานานเท่ากับการทำ spool ให้คำสั่ง I/O แบบอะซิงโครนัสทั้งหมดเท่านั้นก็จะย้อนกลับมายังกลุ่มทำงานหลักได้
วิธีการทำงาน
ถ้าหากคุณเคยเขียนโปรแกรมอะซิงโครนัสมาก่อน คุณอาจมีความรู้สึกว่าการเปลี่ยนแปลงเล็กน้อยกับคำสั่งเว็บเซอร์วิสน่าจะยังไม่พอ ผมยังไม่ได้เข้าไปยุ่งกับอินเทอร์เฟซ IAsyncResult เลย แถมยังไม่ได้บอกให้เพจที่เก็บเว็บเซอร์วิสเอาไว้ทราบว่า ผมได้ทำงานแบบอะซิงโครนัสอยู่ (โดยการจองงานหรือการใช้เทคนิคอื่นๆ) แต่ดูเหมือนมันก็ใช้งานได้เหมือนอย่างที่ผมหวังเอาไว้
ความลับอยู่ตรงที่การใช้ method แบบอะซิงโครนัสของเว็บเซอร์วิสพร็อกซีคลาส รวมทั้งคลาสที่ชื่อ AsyncOperationManager ของ Microsoft .NET Framework 2.0 ที่เข้ามาช่วย ถ้าหากผมเรียก GetNewsHeadlinesAsync method ของพร็อกซีคลาส ระบบจะทำการเทียบการเรียกกับ method ตัวช่วยที่ชื่อ InvokeAsync ซึ่งอยู่ภายในคลาสพื้นฐานที่ชื่อ SoapHttpClientProtocol โดยที่พร็อกซีคลาสก็มาจากที่นี่นั่นเอง InvokeAsync ทำงานสำคัญ 2 ประการก็คือมันจะจองการทำงานอะซิงโครนัสโดยการเรียก CreateOperation method ของ AsyncOperationManager จากนั้นก็เรียกคำสั่งแบบอะซิงโครนัสโดยใช้ BeginGetReguestStream ของคลาส WebRequest
เมื่อถึงจุดนี้จะมีการส่งค่ากลับมา และเพจจะเริ่มต้นการประมวลผลวงจรชีวิตของมัน แต่การที่เพจมีการกำหนดให้ Async เป็น true อยู่ ดังนั้นมันจะยังคงประมวลผลคำสั่งต่อไปผ่านทาง PreRender event และจะมีการส่งงานคำสั่งกลับไปยังกลุ่มทำงานหลังจากที่ทำคำสั่งเก็บแบบอะซิงโครนัสเสร็จแล้ว ระบบจะเรียก method ที่ผมจองเอาไว้กับ GetNewsHeadlinesCompleted event ของพร็อกซีในงานแยกต่างหากซึ่งดึงมาจากกลุ่มงาน I/O ถ้าหากงานนี้เป็นงานอะซิงโครนัสงานสุดท้ายที่ทำเสร็จ (โดยมี AsyncOperationManager คอยเฝ้าติดตามดูอยู่) เพจจะถูกเรียกกลับมา และคำสั่งจะประมวลผลจนเสร็จงานที่ค้างอยู่โดยเริ่มจาก PreRender Complete event ภาพที่ 5 แสดงวงจรชีวิตทั้งหมดเมื่อคุณใช้คำสั่งเว็บอะซิงโครนัสในขอบเขตของเพจแบบอะซิงโครนัส
ภาพที่ 5 คำสั่งอะซิงโครนัสในเพจอะซิงโครนัส
AsyncOperationManager เป็นคลาสที่ถูกออกแบบมาเพื่อใช้ในสภาพแวดล้อมต่างๆสำหรับบริหารคำสั่ง method อะซิงโครนัสโดยตรง ตัวอย่างเช่นถ้าหากผมเรียกเว็บเซอร์วิสแบบอะซิงโครนัสจากแอพพลิเคชัน Windows Forms แล้วละก็ เว็บเซอร์วิสจะเชื่อมโยงกับคลาส AsyncOperationManager ด้วย ความแตกต่างของสภาพแวดล้อมแต่ละแบบก็คือ SyncronizationContext เกี่ยวข้องกับ AsyncOperationManager ถ้าหากสั่งงานในขอบเขตของแอพพลิเคชัน ASP.NET แล้ว SynchronizatioinContext จะถูกกำหนดให้เป็น instance ของคลาส AspNetSynchronizationContext จุดมุ่งหมายหลักตรงนี้ก็คือการคอยเฝ้าติดตามว่ายังมีคำสั่งอะซิงโครนัสอีกมากน้อยขนาดไหนที่ยังรออยู่ ดังนั้นเมื่อคำสั่งเหล่านี้ทำงานเสร็จแล้ว การประมวลผลคำสั่งของเพจก็จะทำต่อไป ในทางตรงกันข้าม ถ้าหากอยู่ในแอพพลิเคชันประเภท Windows Forms แล้ว SynchronizationContext จะถูกกำหนดให้เป็น instance ของคลาส Windows Forms SynchronizatioinContext จุดมุ่งหมายหลักตรงนี้ก็คือการช่วยให้ควบคุมคำสั่งจากงานแบกกราวน์ไปยังงาน UI ได้ง่ายขึ้น
การเรียกใช้ข้อมูลแบบอะซิงโครนัส
ในตอนนี้เราจะย้อนกลับไปหาปัญหาการสร้าง Web Part อันสุดท้ายให้ทำงานแบบอะซิงโครนัสได้ และปัญหาทั่วไปของการเรียกข้อมูลแบบอะซิงโครนัสโดยใช้ ADO.NET โชคไม่ดีที่ไม่มีวิธีที่เท่าเทียมกับกลไกอะซิงโครนัสแบบง่ายๆที่เว็บเซอร์วิสพร็อกซีสามารถนำไปใช้เพื่อเรียกข้อมูลแบบอะซิงโครนัสได้ ดังนั้นผมจึงจำเป็นต้องทำงานเพิ่มเติมอีกเล็กน้อยเพื่อทำให้ Web Part อันสุดท้ายนี้ทำงานแบบอะซิงโครนัสได้ ผมสามารถทำงานกับ method อะซิงโครนัสอันใหม่ในคลาส SqlCommand รวมทั้งคุณสมบัติการทำงานแบบอะซิงโครนัสของ ASP.NET ได้ การใช้ SqlCommand ช่วยให้ในตอนนี้ผมสามารถเรียกคำสั่งแบบอะซิงโครนัสได้โดยใช้ methods ต่างๆดังต่อไปนี้
- IAsyncResult BeginExecuteReader(AsyncCallback ac, object state)
- IAsyncResult BeginExecuteNonQuery(AsyncCallback ac, object state)
- IAsyncResult BeginExecuteXmlReader(AsyncCallback ac, object state)
และในตอนนี้ผมสามารถเรียก method ที่ช่วยทำงานให้เสร็จ หลังจากที่สตริงข้อมูลพร้อมแล้วโดยเริ่มการอ่าน
- SqlDataReader EndExecuteReader(IAsyncResult ar)
- int EndExecuteNonQuery(IAsyncResult ar)
- XmlReader EndExecuteXmlReader(IAsyncResult ar)
ถ้าหากต้องการใช้ method เรียกข้อมูลแบบอะซิงโครนัสเหล่านี้ คุณต้องเพิ่ม "async=true" ลงไปในสตริงเชื่อมต่อเสียก่อน ซึงถ้าหากดูจากตัวอย่างของเรา ผมต้องการใส่ข้อมูลลงไปใน GridView โดยการเชื่อมโยง GridView กับ SqlDataReader ดังนั้นผมจะใช้ BeginExecuteReader method เพื่อเริ่มการเรียกแบบอะซิงโครนัส
นอกจากนั้นเพื่อเป็นการเชื่อมโยงเข้าหาเพจอะซิงโครนัส ASP.NET 2.0 ยังยอมให้คุณจองงานอะซิงโครนัสที่จำเป็นต้องประมวลผลก่อนที่เพจจะทำการแสดงผลเสร็จอีกด้วย โมเดลนี้มีความชัดเจนมากกว่าโมเดลที่ผมใช้กับเว็บเซอร์วิสพร็อกซี และมีความคล่องตัวมากกว่าด้วย ถ้าหากผมต้องการจองงานอะซิงโครนัส ผมจะสร้าง intance ของคลาส PageAsyncTask ขึ้นมา จากนั้นก็สั่งให้มันเริ่มทำงานโดยใช้ตัวแทน 3 ชนิดก็คือ begin handler, end handler และ timeout handler โดยที่ begin handler จำเป็นต้องคืนอินเทอร์เฟซ LAsyncResult กลับมา ซึ่งตรงนี้จะเป็นจุดที่ผมเรียกคำสั่งข้อมูลแบบอะซิงโครนัสโดยใช้ BeginExecuteReader จากนั้นจะมีการเรียก end handler เมื่องานเสร็จแล้ว (เมื่อมีข้อมูลพร้อมที่จะอ่านขึ้นมาได้ในตัวอย่างนี้) เพื่อที่ผมจะนำเอาผลลัพธ์ไปใช้ได้ APS.NET จะเป็นตัวคอยจัดการการเรียก begin handler ขึ้นมา ก่อนที่ปลดระหว่างงานคำสั่งเรียกข้อมูล (ทันทีหลังจากที่ PreRender event ทำงานเสร็จ) ภาพที่ 6 แสดงการอัพเดต Web Part รายงานยอดขาย ซึ่งมีการเรียกข้อมูลแบบอะซิงโครนัสโดยใช้การทำงานอะซิงโครนัสและ BeginExecuteReader อะซิงโครนัสของคลาส SqlCommand
หมายเหตุ: ผมใช้เทคนิคเดียวกันนี้กับการเรียกใช้เว็บเซอร์วิส โดยการใช้ methods อะซิงโครนัสอันอื่นที่อยู่ในพร็อกซีคลาส (ตัวอย่างเช่น BeginGetNewsHeadlines เป็นต้น) ปมเด่นอย่างหนึ่งของเทคนิคนี้ก็คือผมสามารถกำหนด timeout handler ได้ ถ้าหากคำสั่งรีโมทไม่สามารถส่งค่ากลับมาได้ทันเวลา ระบบจะเรียก timeout handler ที่เกี่ยวข้องกันขึ้นมาแทน timeout handler ตัวนี้กำหนดเอาไว้ใน Page directive โดยใช้ AsyncTimeout attribute และกำหนดเวลาปกติเป็น 20 วินาที
สิ่งที่ผมอยากบอกอีกอย่างหนึ่งก็คือสิ่งที่ต่างจากการใช้รูปแบบอะซิงโครนัสที่อิงกับเหตุการณ์ก็คือ เมื่อมีการใช้ Page.RegisterAsyncTask แล้ว ผมไม่จำเป็นต้องแยกไปยังคำสั่งอะซิงโครนัสที่อิงกับผลลัพธ์ของ Page.IsAsync อีกครั้ง การจัดการเพจอะซิงโครนัสใน Web Parts ทำงานได้เป็นอย่างดีในเพจซิงโครนัส แถมยังยอมให้มีการประมวลผล Web Parts แบบคู่ขนานอีกด้วย ความแตกต่างหลักก็คือในเพจซิงโครนัส (เพจที่ไม่ได้กำหนดตัวแปร Async เป็น true) งานหลักของเพจจะไม่ได้ถูกปล่อยกลับมายังกลุ่มทำงานในช่วงการประมวผลลอะซิงโครนัส
ในตอนนี้เมื่อ Web Parts ทั้งหมดสามารถทำงานแบบอะซิงโครนัสได้แล้ว ในตอนนี้ผมสามารถใช้ Web Part เหล่านี้ได้ในเพจใดๆที่เป็นแบบอะซิงโครนัสได้ และผมจะมั่นใจว่าเวลาตอบสนองจะไม่เท่ากับเวลารวมที่ Web Parts ทั้งหมดใช้ในการดึงข้อมูลอีกต่อไป แต่จะเป็นเวลาสูงสุดของ Web Part ตัวใดตัวหนึ่งเท่านั้น การกำหนดให้เพจเป็นแบบอะซิงโครนัสและใช้ Web Parts ให้ทำงานกับ I/O แบบอะซิงโครนัสจะช่วยให้ความสามารถในการขยายเว็บไซต์ดีขึ้นอย่างมาก เนื่องจากเพจจะเปิดโอกาสให้การทำงานหลักให้บริการไคล์เอ็นต์อื่นๆที่รอข้อมูลอยู่ได้
ของแถมอีกอย่างหนึ่งก็คือถ้าหากคุณสร้างไซต์ประตูท่าโดยใช้ ASP.NET 2.0 คุณควรจำเอาไว้ว่าคุณสมบัติอะซิงโครนัสแบบใหม่ทั้งหมดเพิ่งมีอยู่ในซอฟต์แวร์เวอร์ชันนี้ และคุณควรนำเอาคุณสมบัติเหล่านี้ไปใช้ปรับปรุงอัตราการตอบสนองและความสามารถในการขยายระบบของแอพพลิเคชันของคุณเอง ถ้าหากต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับบริการอะซิงโครนัสใน ASP .NET 2.0 ให้คุณเข้าไปดูคอลัมน์ Wicked Code ของ Jeff Prosise นิตยสาร MSDN เดือนตุลาคม ปี 2005