promise设计模式

promise是为了解决Javascript的异步编程导致的‘callback hell’而存在的。

目前有多种设计方式,比较知名的如:promise/deferred(jquery就使用了这个方式),
promise/a+(是es6 promise规范)。

当然我们还是希望使用原生的Promise对象,先看下浏览器的支持度:

可见IE还是重灾区,firefox和chrome很早就已经实现了原生的promise了。当然如果要使用promise,已有不少第三方库的支持,如jquery,Q,Bluebird,Thenjs…
还是比较多的实现,同时值得一提的是推荐使用polyfill的方式来解决,如 es6-promise,或者使用Babel进行es6编译转化也是可以的。

先用一个图说明下什么原理:

promise 原理图

看上去很复杂,其实就是(三个状态的流转):

promise 简单图

分别对应流转,有两个预定义处理句柄:

Promise 处理图

以下摘自MDN的Promise/A+规范说明

属性

Promise.length

长度属性,其值为 1 (构造器参数的数目).

Promise.prototype

表示 Promise 构造器的原型.

方法

Promise.all(iterable)

返回一个promise,当iterable参数里所有的promise都被解决后,该promise也会被解决。

Promise.race(iterable)

返回一个Promise,当iterable参数里的任意一个子Promise被接受或拒绝后,该promise马上也会用子Promise的成功值或失败原因被接受或拒绝。

Promise.reject(reason)

用失败原因reason拒绝一个Promise对象。
Promise.resolve(value)
用成功值value解决一个Promise对象。如果该value为可继续的(thenable,即带有then方法),返回的Promise对象会“跟随”这个value,采用这个value的最终状态;否则的话返回值会用这个value满足(fullfil)返回的Promise对象。

Promise原型

属性

Promise.prototype.constructor

返回创建了实例原型的函数. 默认为 Promise 函数.

方法

Promise.prototype.catch(onRejected)

添加一个否定(rejection) 回调到当前 promise, 返回一个新的promise。如果这个回调被调用,新 promise 将以它的返回值来resolve,否则如果当前promise 进入fulfilled状态,则以当前promise的肯定结果作为新promise的肯定结果.

Promise.prototype.then(onFulfilled, onRejected)

添加肯定和否定回调到当前 promise, 返回一个新的 promise, 将以回调的返回值 来resolve.

示范代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
function asyncFunction() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('Async Hello world');
}, 16);
});
}
asyncFunction().then(function (value) {
console.log(value); // => 'Async Hello world'
}).catch(function (error) {
console.log(error);
});

后面再写下在具体开发中如何使用Promise。

CNUTCon的参会体验

对于容器的了解是从docker开始的(最近一些年docker还是很火的),我对于后端架构以及容器技术属于菜鸟水平,所以此次的体验分享还是注重我个人的理解和想法,大家如果觉得有些理解错误或者粗浅,请谅解。

大体内容可以分为四类:技术前瞻(讲概念),架构体系(讲解决方案),开源项目(设计理念),工具实战(云平台)。干货水货皆有。

一开始主要是redhat的副总裁讲谈下一代的PaaS,中间也介绍了redhat的openshift,以及关于容器安全和标准的一些内容,和容器在Paas中发展重要位置和后续开发模式的转变。由于英语水平有限,所以后续还要关注ppt再研究下,总体还是属于技术前瞻和业界发展的方向性的思索。

后面的CoreOS的演讲者,可能我不专心听吧,像是没有理解什么,发个Slide的链接,貌似是一些etcd和Kubernetes的实践。

接下来主要是京东和大众点评的解决方案的分享,感觉像是docker使用的兼容之前虚拟技术的结合的做法,用到了openstack,zookeeper,NATS,以及对于不了解容器方式的开发者兼容模式。使用的规模还是比较大的,同时没有用到mesos,swarm,Kubernetes用于docker的集群管理,而是自己对于docker集群的实现了管理方案。第二天的百度的分享同样自己做了很多的事情,构建自己的云架构。

下午的阿里百川的分享TAE系统,给我的印象,还是比较深的,从技术方向,到架构方向,到实际解决问题,到实现思想,比较开阔和开放的,不光是对于云平台有一定的思想提升,还是对于方法论的提升。直接附上链接,非常值得一看。

后来也去听了七牛以及华为的OCT,七牛主要是分享了基于容器技术的数据处理主题,在各种系统资源的调度和解决性能瓶颈,以及利用go的并发优势,充分发挥容器在数据处理微服务的能力。华为的OCT听得比较少,只是对于OTC还是蛮兴趣的,主要用于OCI容器标准化的测试,以及业务测试,性能测试等的测试套件,该项目是开源的,一改我之前对于华为技术封闭的看法。附上链接

由于公司新品节的上线缘故,需要远程支援下,所以第一天晚上的会后活动没有报名参加,因此也不知道会有什么收获,后面关注线上社区看有些什么分享。

第二天,一早,google的美女工程师分享了Kubernetes的设计思想和实践,由于可能她长期在国外工作,演讲基本上50%到60%的英文和剩下的中文,给我在理解上造成了一些障碍,其中对于一些原则,还是记忆犹新,简单大于复杂,宠物VS牲口(pets vs cattle),解释下,什么是宠物和牲口,主要是对于线上的服务的思考,宠物般的服务需要呵护和更多的心智来维护保证高可用,由于维护的成本负担比较重,所以宠物般的服务不会很多,而牲口是则是一群服务,虽然很多,但是不需要给每个牲口做做细心呵护,如果出现问题,简单处理,或者直接杀掉,同样也实现了高可用。Kubernetes就是用于管理容器集群的工具,是google的omega的轻量版本,在开源社区还是比较活跃的,后面我估计也会研究下。

之后去听了一个同样是做容器集群管理的mesos是apache旗下的开源项目,演讲的主题偏于实践,主要是讲如何应用该工具,同样以数人云的实践来讲。有一点的实用价值,中间和旁边的一起听的用友小哥聊了下,他说mesos比起Kubernetes来讲更具有企业实际运用价值,同时也比Kubernetes稳定些。后面有空这两个都可以对比使用下。

下午听了vmware的关于 cloud native app的演讲,结合了vmware的产品讲述了开发栈和运维栈结构异同,以及基于容器的一些好的开发实践,以及对于安全和隔离的vmware的实现方案,说下周一会发布这款产品。

由于定了5点的票,所以只能提前走了,走前听了,daocloud郭总的关于微服务架构演进的演讲,结合自身daocloud项目,讲述了构建微服务的开发过程和架构变迁。可能因为daocloud是创业团队,对于架构变迁都是以快速迭代,需求出发的方式,和我的想法十分对位。也讲了docker在微服务的实现起到的重要作用。其中有一点原则很有意义:如果业务的持续集成的速度和演进的复杂程度出现效率问题,才需要做服务的拆分。关于服务的粒度,也做了和SOA的对比。大体微服务粒度在于5-10个人开发的业务复杂度的粒度。其他由于时间问题,稍微听了下coreos的一个分享,双内核,轻量的Linux系统,用于部署容器的系统,也有一些基于coreos封装的工具。

以上是本次参加全球容器大会的大体体验,总的感觉容器技术,百花齐放,各种实现和解决方案,同时大家对于容器技术很大期望,是未来技术变革的一个基点,但同时,还有不少不足和坑需要大家填补,如安全,隔离性,完善的集群管理,热迁移部署等。

关键字:RedHat openshift vmware OCI coreOS mesos Kubernetes swarm oct omega LXC cgroup

题外话

每次参加技术会议,上厕所都有一个奇怪的现象,男厕所需要方便排队,而女厕所却寥寥无几。
妹子出现在会上,十分抢眼。

其他的,后续再整理。

9月29日,回南京的火车上。

关于Javascript继承在es5到es6改变

再重温下之前继承的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var inherits = function(ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return '(' + this.x + ', ' + this.y + ')';
}
function ColorPoint(x, y, color) {
Point.call(this, x, y);
this.color = color;
}
inherits(ColorPoint, Point);
ColorPoint.prototype.toString = function() {
return this.color + ' ' + ColorPoint.super_.prototype.toString.call(this);
}

然而es6则可以这么写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color;
}
toString() {
return this.color + ' ' + super.toString();
}
}

显然es6的写法更加清晰,隐去了Javascript硬去使用prototype和call的实现细节。但是es6并没有完全按照Java ,C++面向对象的语法来实现,
只是对于原型继承的一个语法糖。

nodejs继承的用法

对于上一篇,最后不明白的那块内容,今天在看Object.assign这个Polyfill实现里面有个方法
defineProperty方法,里面也有

1
2
3
4
5
6
{
enumerable: false,
configurable: true,
writable: true,
value:function(){...}
}

解释如下:

configurable

当且仅当这个属性描述符值为 true 时,该属性可能会改变,也可能会被从相应的对象删除。默认为 false。

enumerable

true 当且仅当该属性出现在相应的对象枚举属性中。默认为 false。
数据描述符同时具有以下可选键值:

value

与属性相关的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。

writable

true 当且仅当可能用 赋值运算符 改变与属性相关的值。默认为 false。

另外,Object.create也有说明,create方法的第二个参数的说明:

propertiesObject

一个对象值,可以包含若干个属性,属性名为新建对象的属性名,属性值为那个属性的属性描述符对象.

第一次接触node继承,是在使用events.EventEmitter时接触的。
代码如下:

1
2
3
4
5
6
7
8
var util = require("util");
var events = require("events");//EventEmitter通过events模块来访问
function MyStream() {//新建一个类
events.EventEmitter.call(this);
}
util.inherits(MyStream, events.EventEmitter);

为什么需要Call父级构造函数呢?inherits方法无法继承父级构造函数的成员方法和变量吗?
是的,inherits方法只是把子级的构造函数的原型指向了父级的原型,也就是说,不是父级实例化后的对象
所以没有父级构造函数的成员。

这样的好处是继承的方法只是继承了父级在prototype可以共享的方法和属性,降低了原型链的继承查找。
call则直接指向了父级构造函数,当实例化子级构造函数时,将父级的构造过程运用于子级。

和之前讲的混合方式有点类似,但是从出发点来讲,
更清晰的区分了冒充和原型的信息共享。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function A() {
this.a = 123;
}
A.prototype.say = function() {
return this.a;
}
function B() {
this.b = 456;
}
B.prototype = A.prototype;
var b = new B;
console.log(b.say());//undefined

nodejs继承实现

直接上代码/lib/util.js

第634行

1
2
3
4
5
6
7
8
9
10
11
exports.inherits = function(ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};

从源码看也是使用原型的方式实现了继承,不过在ctor.super_ = superCtor;里面设置了一个super_的属性指向
被继承的构造函数,同时在继承超级构造的原型附加了一些构造的成员属性而创建的对象。

至于constructor里面的这些属性,我目前只能猜猜是用于继承追溯或者构造函数的属性声明。
后面详细研究了node的util其他代码或许会有一个明确的答案。

JavaScript的继承之二

继续上一篇的话题继承,这里主要讲下实际开发的运用。

冒充方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function () {
alert(this.color);
};
}
function ClassB(sColor) {
}
function ClassB(sColor) {
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;
}
function ClassB(sColor, sName) {
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}
var objA = new ClassA("blue");
var objB = new ClassB("red", "John");
objA.sayColor(); //输出 "blue"
objB.sayColor(); //输出 "red"
objB.sayName(); //输出 "John"

这种方式同样也可以多重继承,尽管不推崇,但是有一点可以看出这个继承的灵活性

1
2
3
4
5
6
7
8
9
function ClassZ() {
this.newMethod = ClassX;
this.newMethod();
delete this.newMethod;
this.newMethod = ClassY;
this.newMethod();
delete this.newMethod;
}

callapply方式

这种方式是利用了this这个关键字,实现的函数成员的复制,同时call和apply也可以利用这个原理实现继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function ClassB(sColor, sName) {
//this.newMethod = ClassA;
//this.newMethod(color);
//delete this.newMethod;
ClassA.call(this, sColor);
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}
function ClassB(sColor, sName) {
//this.newMethod = ClassA;
//this.newMethod(color);
//delete this.newMethod;
ClassA.apply(this, new Array(sColor));
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}

prototype方式

1
2
3
4
5
6
7
8
9
function ClassB() {
}
ClassB.prototype = new ClassA();
ClassB.prototype.name = "";
ClassB.prototype.sayName = function () {
alert(this.name);
};

原型链是不支持多重继承。记住,原型链会用另一类型的对象重写类的 prototype 属性。

混合方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function ClassA(sColor) {
this.color = sColor;
}
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB(sColor, sName) {
ClassA.call(this, sColor);
this.name = sName;
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayName = function () {
alert(this.name);
};

继承机制由ClassA.call(this, sColor);
ClassB.prototype = new ClassA();共同作用。
ClassA.call(this, sColor);在 ClassB 构造函数中,用对象冒充继承 ClassA 类的 sColor 属性。
ClassB.prototype = new ClassA();用原型链继承 ClassA 类的方法。由于这种混合方式使用了原型链,所以 instanceof 运算符仍能正确运行。

拷贝方式

1
2
3
4
5
6
7
8
 function extend2(Child, Parent) {
    var p = Parent.prototype;
    var c = Child.prototype;
    for (var i in p) {
      c[i] = p[i];
      }
    c.uber = p;
  }

深拷贝(递归拷贝)

1
2
3
4
5
6
7
8
9
10
11
12
  function deepCopy(p, c) {
    var c = c || {};
    for (var i in p) {
      if (typeof p[i] === 'object') {
        c[i] = (p[i].constructor === Array) ? [] : {};
        deepCopy(p[i], c[i]);
      } else {
         c[i] = p[i];
      }
    }
    return c;
  }

jquery就是采用了拷贝的方式。

JavaScript的继承

很多时候,大家都认为JavaScript是面向对象的编程语言,的确是,但是和Java,C++等的编程语言右不相同的OO做法。
JavaScript 原型一文中已经说明了JavaScript的原型原理。
JavaScript里面的OO大部分是基于原型来实现的。

上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Cat() {
this.name = 'Tom';
this.sex = 'male';
}
Cat.prototype = { className: 'cat' };
function WhiteCat() {
this.color = 'white';
}
WhiteCat.prototype = new Cat();
var cat = new Cat();
var whiteCat = new WhiteCat();
console.log(cat.name);
console.log(cat.sex);
console.log(cat.className);
console.log(whiteCat.name);
console.log(whiteCat.sex);
console.log(whiteCat.className);
console.log(whiteCat.color);

通过原型链,实现对象与对象之间的信息共享,继而实现了继承。这是一种简洁,灵活的方案,下面我们具体看下在实际开发过程中如何运用。

JavaScript 原型

Javascript 里面的一些内建的基础类型,如 Object,Array,Number,Date,String,RegExp
都是基于函数创建的,在Js的世界里,我们把这种函数叫构造函数。
但也有例外,如Math,只是一个对象,它的__proto__Object

1
2
3
4
5
6
console.log(Object.prototype)
//chrome: Object {}
console.log(Object.__proto__);
//chrome: function Empty(){}

__proto__prototype 分别是什么呢,是不是和原型有关?

  • __proto__可以理解为原型的引用或指针
  • prototype可以理解为原型对象

举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
var someone = {
name:'alex',
sex:'male',
job:'happy coder'
}
var otherOne = {
name:'mike'
}
otherOne.__proto__ = soameone;
console.log(otherOne);

很可惜__proto__这个原型引用的属性不是ES6之前的标准,所以IE浏览器是不支持的。

那又怎么实现对象的扩展呢?

prototype一直是标准,那么 prototype又是怎么用于这个场景的呢?

1
2
3
4
5
6
7
8
//给Object增加一个create方法,这个方法创建一个使用原对象作为其原型的新对象
if (typeof Object.create !== 'function') {
Object.create = function(o) {
var F = function() {};//建立一个空的构造函数(或者理解为类)
F.prototype = o;//通过给构造函数的原型对象进行赋值,也就是将作为参数对象赋值给空的构造函数
return new F();//再实例化这个构造函数的对象
}
}

在ES5的时候,已经支持原生的Object.create,如果为了兼容不支持的浏览器,可以使用以上方法。

1
2
var otherOne = Object.create(someone);
otherOne.name = 'Mike';

一个对象的原型引用只能一个,这样就形成了一个链,我们把这个链称之为“原型链”。

对象与对象之间的这个继承关系,是通过这种原型链形成的。

创建JS对象有三种方式:直接量创建,构造函数创建,Object.create创建

  1. 直接量创建最简单
1
2
3
4
5
6
7
8
9
var someone = {
name:'alex',
sex:'male',
job:'happy coder'
}
var num5 = 5;
var array = [1,2,3,5]
  1. 构造函数创建
1
2
3
4
5
6
7
8
function Cat()
{
this.name = 'cat';
this.color = 'black';
}
var cat = new Cat();
  1. Object.create创建
    如上一章节的代码,不再赘述

性能

在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。

注意:不好的实践:扩展原生对象的原型(除非移植较新 JavaScript 引擎的特性)