這篇文章需要對javascript的promise有基本的認識,對不熟的讀者可能不太友善,需要自行google,請大家海涵orz
這一次的教學我們接續上一篇
python的asyncio模組(五):Future對象與Task對象
的內容。
在上一篇結尾我們用了Future對象去模擬javascript的promise物件的行為,但還沒有解釋程式的實際內容,所以就留到這次的教學做解說。
接續上一篇內容
程式我就不貼了,我覺得放在分頁或是放在另一個視窗一起看比較方便。
先把重點放在最末尾的幾行:
loop = asyncio.get_event_loop()
success_future = loop.create_future()
fail_future = loop.create_future()
不像javascript本身內置Event loop,python必須明確的定義出一個Event loop來實行異步程式。
然後我們用loop.create_future創了兩個Future物件,success_future和fail_future,另外要注意一件事,因為Future本身就是用來執行異步程式的,所以在初始化時,就會綁定一個Event loop,也就是Future裏面會有一個內置成員self._loop,也就是我們剛剛創立的loop變數。
success_future.add_done_callback(success_callback)
fail_future.add_done_callback(fail_callback)
我們讓兩個Future物件各自增加一個callback,當我們執行future.set_result()和future.set_exception()的時候(也就是上一篇說的當我們指定了任務的結果),這些callback就會自動執行。
之所以能夠自動執行callback就是因為Future在set_result()或是set_exception()之後會驅動內置成員self._loop去執行先前加入的callback。
loop.call_soon(promise_example, 'success', success_future)
loop.call_soon(promise_example, 'fail', fail_future)
loop.run_until_complete(asyncio.wait([success_future, fail_future]))
之前的文章python的asyncio模組(四):Event loop常用API似乎沒有提到call_soon的用法,那是因為call_soon是比較底層的用法,在asyncio的一般應用情境下,是不會使用到call_soon,也不會真的操作Future對象,但以教學的目的來說,稍微了解這些底層的運作也是重要的。
call_soon(func, *args)會指定一個要執行的function和要放進func的參數,當啟動Event loop時就會直接執行被放入call_soon的func。
最後一行的用法在python的asyncio模組(三):建立Event Loop和定義協程都有提到:
run_until_complete指定某個任務完成就會停止Event loop,可以傳入coroutine或Task或Future。
asyncio.wait把一個任務(可以是coroutine或Task或Future)清單組成一個大的任務。
所以最後一行指的是如果success_future和fail_future都已經被指定任務的結果且執行完各自的callback就會停止Event loop。
接下來我們來把上篇文章的javascript程式和python程式做一個功能上的比對,這樣我們就更能了解我們的Future是如何模擬出promise的功能。
1. Future物件相當於js的promise物件
js程式裡的success_promise和fail_promise相當於python程式裡的success_future和fail_future,雖然結構上有點不一樣,比如說js的promise一開始就直接包住一個function,而Future只是作為一個參數互相傳遞。
但是他們扮演的角色都是差不多的,他們得到了一個結果後就會呼叫對應的callback,像下面第四點和第五點所說的。
2. future._loop.call_later相當於js裡的setTimeout
js程式裡的setTimeout會提醒其內置的Event loop在1000毫秒之後執行setTimeout裏面的callback,而python程式的future則會呼叫其內部變數_loop的一個方法call_later說,在1秒後呼叫setTimeout_func,這是一樣的概念。
call_later也跟call_soon一樣,是比較底層的用法,顧名思義,call_later就是在經過一定時間後才會呼叫某個function。
3. setTimeout_func相當於js中setTimeout裡面放進去的callback
4. future.set_result()相當於js promise裡面的resolve()
5. future.set_exception()相當於js promise裡面的reject()
js程式中setTimeout裡面的callback根據success_or_fail的值來決定呼叫resolve('success')或是reject(new Error('fail')),這就跟python程式中的setTimeout_func,根據success_or_fail的值來決定呼叫future.set_result('success')或是future.set_exception(Exception('fail'))。
6. success_callback融合了js程式裡的success_promise.then和success_promise.catch
7. fail_callback融合了js程式裡的fail_promise.then和fail_promise.catch
歸功於剛剛解說的:
success_future.add_done_callback(success_callback)
fail_future.add_done_callback(fail_callback)
success_future只要被指定結果(set_result或是set_exception)就會呼叫success_callback。
fail_future只要被指定結果(set_result或是set_exception)就會呼叫fail_callback。
然而success_callback和fail_callback全都同時包辦js程式中的then和catch,之所以能同時融合兩種功能是因為success_callback和fail_callback裏面都做了try/catch的處理。
不知道這次文章會寫這麼長,還沒講到Task感覺就已經到了一個篇幅的量。
會囉哩叭唆這麼多,主要還是想好好解釋Future如何在底層實現異步執行,希望大家能看懂。
下一次的教學會講解使用Coroutin和Task對象後如何改善異步程式的可讀性,然後我們會發現一件很重要的事情,Javascript為了改善異步程式的可讀性,從ES6的Promise推進到ES7的async/await,這與Future對象的使用進展到Task對象的使用是類似的概念。
下一篇教學:
python的asyncio模組(七):Future對象與Task對象(三)