forked from Matt-Esch/virtual-dom
-
Notifications
You must be signed in to change notification settings - Fork 7
Open
Description
Transaction
创建出一个黑盒,用于包裹任意方法,使得该方法的包裹在任意时候都正确触发,哪怕该方法在执行过程中抛错,如下图:
wrappers (injected at creation time)
+ +
| |
+-----------------|--------|--------------+
| v | |
| +---------------+ | |
| +--| wrapper1 |---|----+ |
| | +---------------+ v | |
| | +-------------+ | |
| | +----| wrapper2 |--------+ |
| | | +-------------+ | | |
| | | | | |
| v v v v | wrapper
| +---+ +---+ +---------+ +---+ +---+ | invariants
perform(anyMethod) | | | | | | | | | | | | maintained
+----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
| | | | | | | | | | | |
| | | | | | | | | | | |
| | | | | | | | | | | |
| +---+ +---+ +---------+ +---+ +---+ |
| initialize close |
+-----------------------------------------+
How to use?
// ES6 Object.assign,有点像jQuery.extend,具体参见:http://es6.ruanyifeng.com/#docs/object
var assign = require('react/lib/Object.assign'),
Transaction = require("react/lib/Transaction");
// 要包裹的方法
function dosth() {
console.log('dosth');
}
// 创建一个Transaction的子类
function MyTransaction() {
this.reinitializeTransaction();
}
// mixin
assign(
MyTransaction.prototype,
Transaction.Mixin,
{
// 需要实现一个getTransactionWrappers方法,获取需要的包裹
getTransactionWrappers: function() {
return [{
// 第一个包裹的初始化方法
initialize: function (e) {
console.log('init first');
},
// 第一个包裹的完成方法
close: function () {
console.log('close first');
}
}, {
// 第二个包裹的初始化方法
initialize: function (e) {
console.log('init second');
},
// 第二个包裹的完成方法
close: function () {
console.log('close second');
}
}];
}
}
);
var myTransaction = new MyTransaction();
// 包裹dosth方法,scope是this
myTransaction.perform(dosth, this);
// init first
// init second
// dosth
// close first
// close second
从上面的例子,我们可以发现,在被包裹的函数运行前,会先运行wrappers的初始化方法,然后运行被包裹函数,最后运行wrappers结束方法。
// 前面省略
assign(
MyTransaction.prototype,
Transaction.Mixin,
{
getTransactionWrappers: function() {
return [{
initialize: function (e) {
// 让这里抛出错误
dosthundefined();
console.log('init first');
},
close: function () {
console.log('close first');
}
}, {
initialize: function (e) {
console.log('init second');
},
close: function () {
console.log('close second');
}
}];
}
}
);
var myTransaction = new MyTransaction();
myTransaction.perform(dosth, this);
// init second
// close second
// Uncaught ReferenceError: dosthundefined is not defined
从上面的例子,我们可以发现,如果wrapper出现错误,则忽略该wrapper以及被包裹函数。
// 前面省略
function dosth() {
dosthundefined();
console.log('dosth');
}
// 中间省略
myTransaction.perform(dosth, this);
// init first
// init second
// close first
// close second
// Uncaught ReferenceError: dosthundefined is not defined
这个例子我们可以发现,如果被包裹函数出错,不会影响wrappers的运行。
具体实现请参考:https://github.com/facebook/react/blob/master/src/utils/Transaction.js ,比较简单。
Why we need Transaction?
由于虚拟DOM
和DOM
两者是分别更新的,而两者是需要保持强一致性的,否则虚拟DOM中用户已经付款了,但页面却不显示用户付款了这就悲剧了!!!
那么哪里最有可能不可预知的错误,导致两者不一致呢?
回想一下同步逻辑:
JSX + 数据 -> 虚拟DOM -> 算出差异 -> 根据差异更新
错误最后可能出现在:
- 不可预知的数据,或者数据就是错误的,导致虚拟DOM生成时抛错
- 根据差异更新时,节点无法找到,或者DOM不符合预期
而Transaction正是为了解决虚拟DOM
和DOM
同步的一致性而生的。
Metadata
Metadata
Assignees
Labels
No labels