Imagine you have a long task to do, and can only do one thing at a time. Normally, in order to do it, you'd have to stop doing everything else.
// pseudocode
Main() {
DoLaundry()
GoAboutYourDay()
}
DoLaundry() {
// boring parts here
}
Now imagine you want to be able to go about your day while your laundry is being made. One solution would be to get someone else to do it. So you take it to a cleaning shop, tell them what to do, give them your clothes, and tell them to phone you when they're done. In return, they give you back a ticket so they can find your clothes again when you want them back.
// pseudocode
Main() {
ticket = DoLaundry.BeginDoing(CallMeWhenDone)
GoAboutYourDay()
ticket.WaitUntilDone()
}
CallMeWhenDone(ticket) {
cleanLaundry = DoLaundry.FinishDoing(ticket)
}
This is how asynchronous operation works.
BeginInvoke You tell the program what you need to be done (the delegate), what to call when it's done (callback), and what to do it with (state). You get back an IAsyncResult, which is the object that you need to give it back in order to receive your result. You can then do other stuff, or use the WaitHandle in the IAsyncResult to block until the operation's done.
Callback: When the asynchronous operation finishes, it will call this method, giving you the same IAsyncResult as before. At this point, you can retrieve your state object from it, or pass the IAsyncResult to EndInvoke.
EndInvoke: This function takes the IAsyncResult and finds the result of the operation. If it hasn't finished yet, it'll block until it does, which is why you usually call it inside the callback.
This is a pattern that's often used all over the framework, not just on function delegates. Things like database connections, sockets, etc. all often have Begin/End pairs.
MSDN has documentation on the pattern here: http://msdn.microsoft.com/en-us/library/2e08f6yc%28VS.71%29.aspx