這篇文章需要對javascript的promise有基本的認識,對不熟的讀者可能不太友善,需要自行google,請大家海涵orz
上一篇教學python的asyncio模組(四):Event loop常用API講了關於Event loop常用的幾個method,其中介紹了兩個method:
這兩個method一個回傳task對象,一個回傳future對象,上一篇教學我們把他視為類似的東西,但仔細研究後這兩種對象在意義與用途上其實差異不小。
事實上這兩個對象確實是有相似的結構,Task對象是從Future對象繼承過來的,所以Future對象所擁有的method,Task對象也有,但是這兩個對象被發明出來的目的是很不一樣的。
我們大概都了解,創立一個類別的目的,就是將一堆流程與行為或是事物之間的關係抽象化為一個概念,所以一個好的類別,能夠完好的描述一個概念,並能描述這個概念之下相關連的一些行為。
那Future對象是用來描述怎樣的一個概念呢?
顧名思義,future代表未來,很多其他教學文件說Future對象代表一個還未執行或還未完成的任務的結果,而這的確也指涉了一個未來的概念,這是一個在未來才會出現的結果。
也可以從Javascript的Promise來思考這件事,Promise代表一個未來才會完成的操作,或是一個承諾,看看他有兩個method:then還有catch,相當於:
而這也代表著一種未來的概念,或也能說他代表一個未完成的任務,因此他和Future對象想要表達的是很類似的東西。
那這個代表未來的或未完成任務的Future對象會有什麼method呢?
若從一個未完成任務的角度來看,我們對這個Future對象最關心的就是,到底他完成後會得到什麼?會成功還是失敗?完成之後我們還會做什麼?
所以他的method都圍繞這幾個問題而設計:
觀察現在Future的狀態
指定任務的結果
取得任務結果
指定任務完成後續要進行的行為
而Future在實務上的主要用途是設計異步程式,Event_loop會拿到很多待完成和未完成的任務,並一遍又一遍的進行輪詢,而這些任務都要以Future對象的結構加進Event_loop裏面。
上面在講Future的時候發現完全沒有提到Coroutine,這是因為Future的method裏面完全沒有使用到Coroutine,雖然Future是緊貼著asyncio的Event loop而設計的,但這不表示Future需要Coroutine,甚至asyncio的Event loop也不一定要Coroutine才能完成異步程式喔!
Future充其量只是一個描述概念的框架,也制定了一些能被Event loop所使用的基本方法,而Task對象繼承了Future對象的一些基本method,另外其在執行__init__進行初始化的時候,會多傳入一個Coroutine參數。
仔細研究原始碼會發現Task對象裏面有一個非常重要的method叫作_step,他扮演了Coroutine和Event loop的溝通橋樑,對內負責Coroutine的執行,對外又因為繼承了Future的method所以能被Event loop所使用。
簡而言之,Task對象有著Future對象的外殼,能被Event loop所使用,對內又能嵌入Coroutine,讓Coroutine成為這個未完成任務的實際內容。
至於更詳細一點的說明之後會再開一個asyncio源碼解析的系列文章,這裡就不深究下去了。
前面講了一堆概念性的東西,現在用code示範一下Future對象不需要Coroutine也能夠正常運行。
下面我們用Future來簡單模擬javascript裡的promise物件吧!
let promise_example = (success_or_fail) => {
return new Promise((resolve, reject) => {
console.log("Start exec promise_example, success_or_fail === "+success_or_fail);
setTimeout(()=> {
if (success_or_fail === 'success') {
resolve('success');
} else if (success_or_fail === 'fail'){
reject(new Error('fail'));
}
}, 1000);
});
}
let success_promise = promise_example('success');
let fail_promise = promise_example('fail');
success_promise.then((value) => {
console.log('exec success_promise resolve callback');
console.log(value);
}).catch((err) => {
console.log('exec success_promise reject callback');
console.log(err.message);
});
fail_promise.then((value) => {
console.log('exec fail_promise resolve callback');
console.log(value);
}).catch((err) => {
console.log('exec fail_promise reject callback');
console.log(err.message);
});
上面的Javascript程式創建了一個可以產生兩種promise的函數promise_example,其中success_promise會在停住一秒後resolve('success'),另一個fail_promise在停住一秒後reject('fail')。
然後分別對兩種promise串上一個then method和catch method,接下來就不詳述promise的原理了,懇請不熟悉promise的讀者查一下網路的其他教學XD,我們直結揭曉程式執行結果:
Start exec promise_example, success_or_fail === success
Start exec promise_example, success_or_fail === fail
exec success_promise resolve callback
success
exec fail_promise reject callback
fail
那如果要用asyncio的Future去實作一模一樣的功能,那會像以下的程式:
# python3.5
# ubuntu 16.04
import asyncio
def promise_example(success_or_fail, future):
print("Start exec promise_example, success_or_fail === "+success_or_fail)
future._loop.call_later(1, setTimeout_func, success_or_fail, future)
def setTimeout_func(success_or_fail, future):
if success_or_fail == 'success':
future.set_result('success')
elif success_or_fail == 'fail':
future.set_exception(Exception('fail'))
def success_callback(future):
try:
if future.result() is not None:
print('exec success_promise resolve callback')
print(future.result())
except Exception as e:
print('exec success_promise reject callback')
print(e)
def fail_callback(future):
try:
if future.result() is not None:
print('exec fail_promise resolve callback')
print(future.result())
except Exception as e:
print('exec fail_promise reject callback')
print(e)
loop = asyncio.get_event_loop()
success_future = loop.create_future()
fail_future = loop.create_future()
success_future.add_done_callback(success_callback)
fail_future.add_done_callback(fail_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語法,可能會增加過多的篇幅,所以第五篇的系列文就先到這篇吧!剩下的內容會在第六篇繼續探討。
下一篇教學:
python的asyncio模組(六):Future對象與Task對象(二)
參考資料:
python Task對象官方文件說明
https://docs.python.org/3.5/library/asyncio-task.html
Future對象的使用
https://medium.com/@lanf0n/%E5%BE%9E-asyncio-%E9%96%8B%E5%A7%8B-callback-c60a74c54743