前言
这篇文章需要对javascript的promise有基本的认识,对不熟的读者可能不太友善,需要自行google,请大家海涵orz
上一篇教学python的asyncio模组(四):Event loop常用API讲了关于Event loop常用的几个method,其中介绍了两个method:
loop.create_task(coro)loop.create_future()这两个method一个回传task对象,一个回传future对象,上一篇教学我们把他视为类似的东西,但仔细研究后这两种对象在意义与用途上其实差异不小。
事实上这两个对象确实是有相似的结构,Task对象是从Future对象继承过来的,所以Future对象所拥有的method,Task对象也有,但是这两个对象被发明出来的目的是很不一样的。
Future对象的意义
我们大概都了解,创立一个类别的目的,就是将一堆流程与行为或是事物之间的关係抽象化为一个概念,所以一个好的类别,能够完好的描述一个概念,并能描述这个概念之下相关连的一些行为。
那Future对象是用来描述怎样的一个概念呢?
顾名思义,future代表未来,很多其他教学文件说Future对象代表一个还未执行或还未完成的任务的结果,而这的确也指涉了一个未来的概念,这是一个在未来才会出现的结果。
也可以从Javascript的Promise来思考这件事,Promise代表一个未来才会完成的操作,或是一个承诺,看看他有两个method:then还有catch,相当于:
我承诺未来完成某操作后会再继续完成某事 --> then method在做的事情我承诺未来若某操作失败会做怎样的处理 --> catch method在做的事情而这也代表着一种未来的概念,或也能说他代表一个未完成的任务,因此他和Future对象想要表达的是很类似的东西。
那这个代表未来的或未完成任务的Future对象会有什么method呢?
若从一个未完成任务的角度来看,我们对这个Future对象最关心的就是,到底他完成后会得到什么?会成功还是失败?完成之后我们还会做什么?
所以他的method都围绕这几个问题而设计:
观察现在Future的状态
done() 察看这个任务是否已经完成(成功或失败)cancelled() 察看这个任务是否已经被取消指定任务的结果
cancel() 取消任务的执行set_result() 判定任务成功,并指定执行完的结果set_exception() 判定任务失败,并指定途中出现的exception取得任务结果
result() 取得任务成功时的结果,若任务未成功则为Noneexception() 取得任务失败时的exception,若任务未失败则为None指定任务完成后续要进行的行为
add_done_callback() 指定若任务完成后要执行的callbackremove_done_callback() 取消若任务完成后要执行的callback而Future在实务上的主要用途是设计异步程式,Event_loop会拿到很多待完成和未完成的任务,并一遍又一遍的进行轮询,而这些任务都要以Future对象的结构加进Event_loop裏面。
Task对象的意义
上面在讲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源码解析的系列文章,这里就不深究下去了。
Future对象的使用
前面讲了一堆概念性的东西,现在用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 === successStart exec promise_example, success_or_fail === failexec success_promise resolve callbacksuccessexec fail_promise reject callbackfail
那如果要用asyncio的Future去实作一模一样的功能,那会像以下的程式:
# python3.5# ubuntu 16.04import asynciodef 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