2010-09-13 8 views
7

एफ # में मैं जानता हूँ कि Async.AwaitEvent का उपयोग कर एक घटना के लिए एसिंक्रोनस रूप से प्रतीक्षा करने के लिए कैसे:एक साथ एफ # में कई घटनाओं में से किसी भी घटना के लिए प्रतीक्षा करें

let test = async { 
    let! move = Async.AwaitEvent(form.MouseMove) 
    ...handle move... } 

मान लीजिए मैं के लिए या तो MouseMove या KeyDown घटना इंतजार करना चाहते हैं। मुझे ऐसा कुछ चाहिए:

let! moveOrKeyDown = Async.AwaitEvent(form.MouseMove, form.KeyDown) 

यह फ़ंक्शन मौजूद नहीं है लेकिन ऐसा करने का कोई और तरीका है?

+4

सावधान रहें! जब आप 'AwaitEvent' और' let! 'के साथ' Event.xyz' combinators का उपयोग करके बनाई गई घटनाओं का उपयोग करते हैं, तो आप एक मेमोरी लीक बना सकते हैं (जब आप लूप में प्रतीक्षा करते हैं)। यदि आप एसिंक्रोनस वर्कफ़्लोज़ के साथ संयोजक जोड़ना चाहते हैं तो आपको ** हमेशा 'ईवेंट' के बजाय 'अवलोकन योग्य' मॉड्यूल का उपयोग करना चाहिए। अधिक जानकारी के लिए मेरा उत्तर देखें ... –

उत्तर

11
let ignoreEvent e = Event.map ignore e 

let merged = Event.merge (ignoreEvent f.KeyDown) (ignoreEvent f.MouseMove) 
Async.AwaitEvent merged 

संपादित करें: एक और संस्करण है कि मूल प्रकार

let merged = Event.merge (f.KeyDown |> Event.map Choice1Of2) (f.MouseMove |> Event.map Choice2Of2) 
Async.AwaitEvent merged 

संपादित 2 को बरकरार रखता है: टॉमस Petricek की टिप्पणी के अनुसार

let e1 = f.KeyDown |> Observable.map Choice1Of2 
let e2 = f.MouseMove |> Observable.map Choice2Of2 
let! evt = Observable.merge e1 e2 |> Async.AwaitObservable 

AwaitObservable primitiv ई here ('टॉमस पेट्रीसेक द्वारा' सिल्वरलाइट में प्रतिक्रियाशील डेमो 'से लिया जा सकता है)।

+0

कूल, थोड़ा और एफ # सीखा। धन्यवाद। –

+3

क्या आप 'ईवेंट' के बजाय 'अवलोकनयोग्य' का उपयोग करने के लिए कोड को बदलने पर विचार कर सकते हैं? (इस परिदृश्य में 'Event.xyz' का उपयोग करके लीक हो सकती है - अधिक जानकारी के लिए मेरा उत्तर देखें ...) –

+0

आप यह ध्यान रखना चाहेंगे कि' Async.AwaitObservable' F # में नहीं बनाया गया है और यह कि "रियल" में एक एक्सटेंशन है - वर्ल्ड फंक्शनल प्रोग्रामिंग। " – gradbot

0

आप Event.map और Event.merge के संयोजन का उपयोग कर सकते हैं:

let eventOccurs e = e |> Event.map ignore 
let mouseOrKey = Event.merge (eventOccurs frm.MouseMove) (eventOccurs frm.KeyDown) 

तो फिर तुम इस नई घटना के साथ Async.AwaitEvent उपयोग कर सकते हैं। यदि MouseMove और KeyDown एक ही प्रकार का था, तो आप Event.map चरण छोड़ सकते हैं और बस उन्हें सीधे मर्ज कर सकते हैं।

संपादित

लेकिन टॉमस बताते हैं, आप Event लोगों को वरीयता में Observable combinators का उपयोग करना चाहिए पर।

+0

यह काम करता है लेकिन अब मैं घटना गुणों के साथ कुछ भी करने की क्षमता खो देता हूं क्योंकि घटना प्रकार 'इकाई' है। शायद मुझे एक पूरी तरह से अलग दृष्टिकोण लेने की जरूरत है, लेकिन मुझे यकीन नहीं है कि क्या। –

+1

आप इसे अनदेखा करने के बजाय चॉइस (या किसी अन्य प्रकार) पर ईवेंट तर्कों को मैप कर सकते हैं। मैंने इस संस्करण के लिए नमूना शामिल करने के लिए अपना उत्तर संपादित किया है – desco

4

क्या हो रहा है यह समझने के हित में मैंने Event.map, Event.merge और Choice पर स्रोत कोड देखा।

type Choice<'T1,'T2> = 
    | Choice1Of2 of 'T1 
    | Choice2Of2 of 'T2 

[<CompiledName("Map")>] 
let map f (w: IEvent<'Delegate,'T>) = 
    let ev = new Event<_>() 
    w.Add(fun x -> ev.Trigger(f x)); 
    ev.Publish 

[<CompiledName("Merge")>] 
let merge (w1: IEvent<'Del1,'T>) (w2: IEvent<'Del2,'T>) = 
    let ev = new Event<_>() 
    w1.Add(fun x -> ev.Trigger(x)); 
    w2.Add(fun x -> ev.Trigger(x)); 
    ev.Publish 

इसका मतलब है कि हमारा समाधान 3 नई घटनाएं बना रहा है।

async { 
    let merged = Event.merge 
        (f.KeyDown |> Event.map Choice1Of2) 
        (f.MouseMove |> Event.map Choice2Of2) 
    let! move = Async.AwaitEvent merged 
} 

हम इस लाइब्रेरी कोड का कसकर युग्मित संस्करण बनाकर इसे एक ईवेंट में कम कर सकते हैं।

type EventChoice<'T1, 'T2> = 
    | EventChoice1Of2 of 'T1 
    | EventChoice2Of2 of 'T2 
    with 
    static member CreateChoice (w1: IEvent<_,'T1>) (w2: IEvent<_,'T2>) = 
     let ev = new Event<_>() 
     w1.Add(fun x -> ev.Trigger(EventChoice1Of2 x)) 
     w2.Add(fun x -> ev.Trigger(EventChoice2Of2 x)) 
     ev.Publish 

और यहां हमारा नया कोड है।

async { 
    let merged = EventChoice.CreateChoice form.MouseMove form.KeyDown 
    let! move = Async.AwaitEvent merged 
} 
12

मैं एक विधि है कि आप talk about reactive programming कि मैं लंदन में था में अपने नमूना में उपयोग के एक कार्यान्वयन के लिए इस्तेमाल किया (वहाँ पृष्ठ के तल पर एक डाउनलोड लिंक है)। यदि आप इस विषय में रुचि रखते हैं, तो आप इस बात को भी उपयोगी पाते हैं :-)।

मैं जिस संस्करण का उपयोग कर रहा हूं वह के बजाय IObservable लेता है (इसलिए विधि का नाम AwaitObservable है)।गंभीर मेमोरी लीकEvent.merge (और Event मॉड्यूल के अन्य संयोजक) का उपयोग करते हुए AwaitEvent के साथ, इसलिए आपको Observable.merge आदि और AwaitObservable का उपयोग करना चाहिए।

समस्या को अधिक विस्तार से वर्णित किया गया है here (स्पष्ट उदाहरण के लिए खंड 3 देखें)। संक्षेप में - जब आप Event.merge का उपयोग करते हैं, तो यह स्रोत ईवेंट (जैसे MouseDown) पर हैंडलर को जोड़ता है, लेकिन AwaitEvent का उपयोग करके प्रतीक्षा करने के बाद यह हैंडलर को नहीं हटाता है, इसलिए ईवेंट कभी नहीं हटाया जाता है - यदि आप एक लूप को कोडिंग में प्रतीक्षा करते रहते हैं एसिंक्रोनस वर्कफ़्लो, आप नए हैंडलर जोड़ते रहते हैं (जो रन करते समय कुछ भी नहीं करते हैं)।

एक साधारण सही समाधान (क्या DESCO पोस्ट के आधार पर) इस प्रकार दिखाई देगा:

let rec loop() = async { 
    let e1 = f.KeyDown |> Observable.map Choice1Of2 
    let e2 = f.MouseMove |> Observable.map Choice2Of2 
    let! evt = Observable.merge e1 e2 |> Async.AwaitObservable 
    // ... 
    return! loop() } // Continue looping 

Btw: तुम भी this article (मेरी किताब से 16 अध्याय के आधार पर) को देखने के लिए चाहते हो सकता है।

+0

दिलचस्प। यह वास्तव में आपकी पुस्तक के ch.16 पढ़ने के दौरान था कि यह सवाल आया था। मैंने सोचा कि एक तरीका होना चाहिए कि एफ # कोर ने किसी भी तरह से इस परिदृश्य के लिए समर्थन प्रदान किया और ऐसा प्रतीत होता है। लेख की धारा 3 के माध्यम से पढ़ना, मैं समझता हूं कि समस्या क्या है। मुझे इस बारे में निश्चित नहीं है कि क्या अवलोकन मेमोरी लीक को रोकने के लिए धारा 6 में वर्णित पिछड़ी-संदर्भ तकनीक का उपयोग करते हैं? या वे किसी अन्य तकनीक का उपयोग करते हैं? –

+0

@ रोनाल्ड: ऑब्जर्वेबल्स संदर्भित पेपर में वर्णित एक अलग तकनीक का उपयोग करते हैं। संक्षेप में (मेरे पास अभी बहुत समय नहीं है) - जब आप एक अवलोकन (जैसे 'मानचित्र' का उपयोग करके बनाया गया) सुनना शुरू करते हैं, तो यह एक टोकन देता है जिसका उपयोग मूल ईवेंट स्रोत से पंजीकरण रद्द करने के लिए किया जा सकता है। –

+0

@ टोमासपेट्रिसक - http://tomasp.net/academic/event-chains/event-chains.pdf लिंक अब टूटा हुआ प्रतीत होता है। क्या पीडीएफ कहीं और स्थानांतरित हो गया है? – rmunn

संबंधित मुद्दे