确定的世界 - The Promise's World
2015-08-10 17:35
260 查看
确定的世界 - The Promise’s World
—— Promise的逻辑以及在Lua中的实现本文基于如下项目(ES6 Promise in lua v1.0.1):
https://github.com/aimingoo/Promise
有这样一个世界
有这样的一个世界在持续地向前推进着,这个世界充满着无穷多个选择,也就是说有无穷多的可能;但对每一个选择来说,存在决定/未决两种状态,如果已经决定,则只能有yes/no两种结果之一。这个世界看起来就是下面这个样子:那么,这个世界是确定的么?
首先,这个世界是有状态的,那些看起来存在y/n两个分支的节点,就存在于这种状态——选择还没有发生,就还有可能。
然而无论某一个节点的选择是什么,这个的世界的结果(的规模)是确定的,它必然演进到世界最下层中的状态的某一个。只不过这个最下层的规模足够大,所以在世界中的“未决”因素足够多的情况下,这个世界就看起来“一切皆有可能”而已。
而这也就是Promise’s World,确定的世界。
什么是已决定的?
到底什么是已“确定”的呢?当你举起手枪对着自己的头的时候,这个行为是“确定”的吗?答案是:这个行为是“确定”的,你只是没有“决定”而已。这个称为“用枪瞄准自己的头”的系统很确定:要么是死,要么是不死——死或不死都是确定的。在语言中,你可以理解为:布尔值是确定的值,它确定的是yes/no。
布尔值被理解为一个“已决定(它的结果行为)的”系统。同理,所有值类型的数据,所有有已决定结果的数据,都是确定的。这在promise中被这样定义下来:
[code]local function promised_y(self, onFulfilled) -- ... end -- ... -- promise for basetype local number_promise = setmetatable({andThen = promised_y}, promise) local true_promise = setmetatable({andThen = promised_y, true}, promise) local false_promise = setmetatable({andThen = promised_y, false}, promise) number_promise.__index = number_promise nil_promise.andThen = promised_y
Ok,这里的代码的意思是说,number/string/boolean,以及nil这些值的行为(andThen)都是已决定的,指向promised_y()。
javascript的promise规范中,这里被称为.then()方法,而拥有这样一个方法的对象被称为thenable object。这个方法有确定的接口:
function (onFulfilled, onRejected)
但在lua中由于then是保留字,所以只好用andThen作为方法名(也有用next来作为方法名的)。
同样,一个对象(lua中的table)也是已决定的,在promise中它与一般的value并没有不同。因此,在lua中的Promise.resolve(value)实现为如下:
[code]function Promise.resolve(value) local valueType = type(value) if valueType == 'nil' then return nil_promise elseif valueType == 'boolean' then return value and true_promise or false_promise elseif valueType == 'number' then return setmetatable({(value)}, number_promise) elseif valueType == 'string' then return value elseif (valueType == 'table') and (value.andThen ~= nil) then return value.catch ~= nil and value -- or, we can direct return value or setmetatable({catch=promise.catch}, {__index=value}) else return setmetatable({andThen=promised_y, value}, promise) end end
留意这里的string类型,它与其它value略有区别,是因为string类型在lua中正好是有meta type的,因此可以直接通过修改元表来让它“变得与promise object”行为一样。至于其它,就必须包装一下了。
对于对象(object/table)来说,它可能有三种情况:
如果为thenable object(即,有.andThen而没有.catch方法),则给他一个catch()方法;
如果为promise object,则直接返回;
如果为普通object(即,其它样式的lua table),则包装成promise object并返回。
那么,什么是promise object呢?
promise object and Promise class
按照协议,promise框架必须实现promise object和Promise class。参考:ECMAScript 2015(ES6),包括如下这些类方法:Promise.new(func)
Promise.resolve(value)
Promise.reject(reason)
Promise.all(arr)
Promise.race(arr)
以及对象方法:
promise:andThen(onFulfilled, onRejected)
Promise:catch(onRejected)
五种类方法之任一都将得到一个promise object。确切地说,你也只能通过这五种方法来得到promise object,哪怕只是数字1,也应当这样来写:
[code]local promise_number_1 = Promise.resolve(1)
这些类方法有些“潜在的/隐式地”将值变成promise的能力,例如:
Promise.all(arr)
严格地来说all()要处理的是一个promise object array,为了这个目的,事实上它会将arr中的每个成员都尝试转换(resolve)以得到promise object。因此下面两种方法:
[code]Promise.all([1,2,3]):andThen(..) -- vs. Promise.all([ Promise.resolve(1), Promise.resolve(2), Promise.resolve(3) ]):andThen(..)
事实上是一样的效果。
而具体到一个promise object,它在lua中描述的结构是一个table(array)。初始化时,它只有一个元素(我通常称之为p[1]):
[code]a_promise_instance = { PENDING }
PENDING表明这个promise是未决的。如果已决定,例如上面提到过的“所有的”值,那么p[1]就存放的是那个具体的值。例如:
[code]-- boolean promise object {true}, {false} -- number promise object {1}, {1.23}, .. -- object promise object {{}}, {obj}, .. -- userdata/function/coroutine/.. {userdata}, {func}, {co}, .. -- nil promise object {} -- string is string, ^^. 'abcd' -- non-promised promise object {PENDING}
在5种Promise类方法中,promise.resolve()和reject()得到的是一个已决定的、值的promise对象(或者,当传入promise object时,返回的是它自身,注1)。而其它三种方法,得到的都将是一个non-promised的对象——也就是说,这些情况下p[1]存放的是PENDING。
注1: 在JavaScript中,这种情况仍将得到一个新的promise,而lua中得到传的的promise object。这并不会带来使用效果上的差异。
推迟决策:lazy resolver
到目前为止,在我们具体讨论PENDING之前,所谓的promise object,以及整个的promise’s world其实都很简单。但仔细看去,这也不过是最开始所描绘的世界中左边的那一半而已——这一半都是promised,是确知的,已决定的。整个Promise世界的秘密(或魔法、活力)都在右边那一部分。也就是p[1]中写着PENDING的未决的那些结点。这些结点是推迟决策的,它什么时候到来是未知的,由于状态未决,所以也有不可确知的结果。仍然以那把瞄着你的头的枪为例,板机并不是你在扣着,而是在1公里以外的一个狙击手的手上。现在(当下)的问题是,你处于PENDING状态,既不知道那个狙击手是不是已经被第三者干掉了,还是已经扣下了板机而子弹是在飞过来的路上。PENDING这个状态是未决的,它未决的是你的生死,直到p[1]被填上一个值——如同子弹真正地射入你的头脑,或者邦德站在你的面前告诉你说“他死了”。
p[1]将被填入“射击”这个行为发生的结果(value/result),或这个行为没有发生的原因(reson)。无论二者之一为何,这个p[1]都是你现在(当下)所不确知的,所以尽管我们有千般主意,也只能等待value/reson两种结果被确认。这“千般主意”我们都可以一一想好,并且用promise:andThen()关联给这个promise,但……我们就是得等着结果发生。所以,Promise in lua为此设计了lazy对象,每一个用andThen添加进来的“主意”都是一个lazy,被追加到promise object数组的末尾——好的,我想你已经知道了,就是p[2]..p
,或称之p[2..n]。
p[2..n]是一个个独立的lazy object。每个lazy表达为三个元素的数组:
[code]{promise, onFulfilled, onRejected}
由于一个PENDING promise是未决的,所以当它决定时至少要做的(第一个) 行为就是将真正的结果填入p[1]。所以由new/all/race这三种方法来创建的(未决的)promise的内部都会调用resolver()来实施这一决策:
[code]function Promise.all(arr) local this, result, count = setmetatable({PENDING}, promise), {}, #arr -- ... resolver(this, result, sure) -- ... end function Promise.race(arr) local this, result, count = setmetatable({PENDING}, promise), {}, #arr -- ... resolver(this, result, sure) -- ... end function Promise.new(func) local this = setmetatable({PENDING}, promise) pcall(func, function(value) return resolver(this, value, true) end, function(reason) return resolver(this, reason, false) end) -- ... end
而这里的“决策(resolver)”,不过是对确定的结果(value)采用确定的行为——将p[1]赋值,并重置andThen()方法:
[code]local function resolver(this, resolved, sure) -- ... this[1], this.andThen = resolved, sure and promised_y or promised_n -- ... end
以及推进所有p[2..n]中的行为:
[code]local function resolver(this, resolved, sure) -- ... for i, lazy in next, this, 1 do -- 2..n pcall(resolver, lazy[1], promised(resolved, (sure and lazy[2] or lazy[3])), sure) this[i] = nil end -- ...
而已。然而考虑到promise规范中允许andThen()返回一个non-promised的promise object,因此resolver()将检测这种状态,并将与这个promise object对应的lazy添加到尾部。
最后的promised
现在,promise’s world中的结点要么是已决的(promised promise)。这种情况下它可能是一个一般值转换过来(Promise.resolve)的,因而只有左侧的边(promise_y),也可能是一个promised promise object,因此具有两条边之一。无论如何,这样的一个promise object的p[1]存放着确定的值(value),而andThen()指向一个确定有结果的行为:promised_y、promised_n,或promised_s。而这三个行为都必然是最终确定的:promised()[code]local function promised(value, action) local ok, result = pcall(action, value) return ok and Promise.resolve(result) or Promise.reject(result) end local function promised_s(self, onFulfilled) return onFulfilled and promised(self, onFulfilled) or self end local function promised_y(self, onFulfilled) return onFulfilled and promised(self[1], onFulfilled) or self end local function promised_n(self, _, onRejected) return onRejected and promised(self[1], onRejected) or self end
要么,就是还未决定的(non-promised promise)。因此它的p[1]中写着PENDING,andThen()指向一个将一切未知塞到p[2..n]的函数——既不是左边的y,也不是右边的n。
而这,就是promise’s world的全部了:
做你所决定的,为那些你所不能决定的做准备。
这一切,要等到PENDING发生变化,推迟决策生效(resolver过程启动)时才会有结果——所以resolver()函数是唯一在yes/no之外,你能看到有promised()调用的地方,那是未来将会发生的一次promised。一旦发生,non-promised was promised。
示例
有一个简单的示例,然而绝大多数lua promise框架都run不过。试试看吧:[code]--- -- A完成后,根据a做BCD三件事,再根据BCD的结果做E。 --- Promise = require('Promise') A = function() return 10 end B = function(a) print(a * 2) end C = function(a) print(a * 4) return Promise.resolve('ok') end D = function(a) print(a * 3) end E = function(result) local b, c, d = unpack(result) print(b, c, d) return Promise.reject('FIRE') end -- promise_A = Promise.resolve(A()) promise_A = Promise.new(function(resolve, reject) local ok, result = pcall(A) return (ok and resolve or reject)(result) end) promise_B = promise_A:andThen(B) promise_C = promise_A:andThen(C) promise_D = promise_A:andThen(D) promises = {promise_B, promise_C, promise_D} Promise.all(promises) :andThen(E) :catch(function(reson) print(reson) end)
相关文章推荐
- 大数据存储之分布式文件系统(一)
- Java文件拆分与合并
- 安装 Django
- Fragment:关于Avoid non-default constructors in fragments的错误
- 十大经典数据挖掘算法之C4.5算法
- 趣味数学应用:求小球在一条斜线上的运动轨迹
- excel里怎样根据两个表里面相同的列,合并其他列到同一个表里
- solr 分页传入太大条数
- [HDU 4507] 吉哥系列故事——恨7不成妻 数位dp
- hdu1394Minimum Inversion Number逆序数
- 浅谈单片机程序设计中的“分层思想”
- 《Maven实战》整理
- CIFAR-10 Competition Winners: Interviews with Dr. Ben Graham, Phil Culliton, & Zygmunt Zając
- C#图片水印类
- python中的exec()函数的作用
- C#实现的XML操作类实例
- HDOJ-2523-矩形A+B
- 02_06 JSP内置对象之session
- iOS开发 应用iOS特性实现在应用内打电话,发短信,打开网页等。实现应用间授权以后再整理
- RunLoop 学习