1

引言

Werner Vogels在主题演讲上简要叙述了Lambda设计思路的由来。

Vogels首先抛出一个问题:什么是云计算的根本(primitives)?

答案:云计算是一个执行环境。

Vogels再抛出第二个问题:什么是应用的根本?

答案:函数(functions,即业务逻辑的载体)+数据(data,即跟业务相关的输入与输出),以及这两者之间的交互——即事件(events。常见的事件如增加、变更、删除等)。

换言之,对于一个应用来说,除了functions、data、events这三个东西是根本之外,其他无论什么代码和框架,无非都是胶水或者UI罢了。

既然如此,理想的情况是用最少的时间写胶水,将做多的时间投入到应用的核心当中。

而最常见的胶水代码,就是触发器(trigger):当发生一个事件(event)时,执行某个函数(function),输出新的数据(data)。Vogels以Excel表单为例:在一个表单当中,一个单元格数值的变更,触发总和列对应单元格数值的变更,就是一个事件触发函数将新的变量纳入计算得出新的数值的过程。这个过程是自动的,还可以是并发的,即一处变更同时触发多个函数。

基于这个思路,AWS做了Lambda服务。

从编程语言角度审视Vogels的观点

在编程语言中,函数(functions)+数据(data)+事件(events)的有机组合就是类(class)。在ES6中、类(class)的主体只能包含方法,不能包含数据属性。从一般的编程理论中也明确阐述,类变量或称为数据(data)应该用访问方法(如getter和setter)进行封装,以保证数据的完整性和一致性。

Vogels以Excel表单举例,说明当数据(data)变化后,触发函数执行;更多人会用react的stats举例,当数据(data)变化后,触发DomTree刷新。这是受数据驱动编程的影响?我只能理解为流派之争。怎么着你都得写个listener吧?listener也是函数!

回到正题,既然应用的本质都是一个类(class),当然最好的API接口也是一个类(class)。当类变量或称数据(data)只能通过类函数访问时,正好符合ES6中的类(class)定义。ES6中的类(class)只包含类函数(functions),其中一部分函数(functions)被其它程序直接调用,另一个部分函数(functions)被事件激活调用。

API类(class)是单例对象(singleton object)

你new与不new,API单例对象都在那里,不增也不减。

这里借用scala语言中对单例对象的叙述:“类和单例对象间的一个差别是,单例对象不带参数,而类可以。因为你不能用new关键字实例化一个单例对象,你没机会传递给它参数。每个单例对象都被作为由一个静态变量指向的虚构类:synthetic class的一个实例来实现,因此它们与Java静态类有着相同的初始化语法。Scala程序特别要指出的是,单例对象会在第一次被访问的时候初始化。”

与Java语言一样,在ES6中也没有单例对象(singleton object)的定义方法。而要记住的是对每一个调用接口的程序而言,实现的接口(API)类是个单例对象。

API类(class)三个基本问题(直接上结论)

  1. 跨域访问 - 支持

  2. 浏览器中调用 - 支持

  3. nodejs中调用 - 支持

编写第一个接口API类 - HelloWorld.es6

class HelloWorld {
    constructor() {
        this.greeting = 'Hello World!';
    }
    welcome(callback) {
        callback(null, this.greeting);
    }
}

export default HelloWorld;

使用babel转成ES5

$ babel HelloWorld.es6 -o HelloWorld.js

现在要把你写好的class发布出去了!

# npm install nodeway -g
# nodeway --class HelloWorld.js --host 0.0.0.0 --port 8080 --docs . &

这句的意思是启动一个Web Server,把HelloWorld.js发布出去。好了,现在剩下的就是测试了。

编写测试程序 index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>HelloWorld</title>
    <script type="text/javascript" src="/HelloWorld.js"></script>
</head>
<body>
<script>
var cb = function(err, greeting) {
    document.write(greeting+'<br/>');
};
var api = new HelloWorld.default;
api.welcome(cb);
</script>
</body>
</html>

用浏览器访问你写的这个index.html文件,就可以看到你发布成功了。

在HelloWorld.es6实现事件(events),需要增加两个函数emit和on。(这段只是原理性展示,实际代码不要这样写)

class HelloWorld {
    constructor() {
        this.greeting = 'Hello World!';
        // 以下是新添加代码,6秒发一个'again'事件
        this.events = {};
        setInterval(() => {
            this.emit && this.emit('again', null, this.greeting);
        }, 6000);
        // 以上是新添加代码
    }
    // 以下是新添加代码
    on(eventName, fn) {
        this.events[eventName] = fn;
    }
    emit() {
        let args = Array.from(arguments),
            fn = this.events[args.shift()];
        fn && fn.apply(this, args);
    }
    // 以上是新添加代码
    welcome(callback) {
        callback(null, this.greeting);
    }
}

export default HelloWorld;

一点基础知识 - ES6原型链

ES6原型链

为了不暴露emit代码,将其放到函数on中(参考ES6原型链)。

class HelloWorld {
    constructor() {
        this.greeting = 'Hello World!';
        // 以下是新添加代码,6秒发一个'again'事件
        this.events = {};
        setInterval(() => {
            this.emit && this.emit('again', null, this.greeting);
        }, 6000);
        // 以上是新添加代码
    }
    // 以下是新添加代码
    on(eventName, fn) {
        this.events[eventName] = fn;
        this.constructor.prototype.emit ||
        (this.constructor.prototype.emit = function() {
            let args = Array.from(arguments),
                fn = this.events[args.shift()];
            fn && fn.apply(this, args);
        });
    }
    // 以上是新添加代码
    welcome(callback) {
        callback(null, this.greeting);
    }
}

export default HelloWorld;

修改测试程序 index.html,实现事件(events)。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>HelloWorld</title>
    <script type="text/javascript" src="/HelloWorld.js"></script>
</head>
<body>
<script>
var cb = function(err, greeting) {
    document.write(greeting+'<br/>');
};
var api = new HelloWorld.default;
api.welcome(cb);
// 以下是新添加代码
api.on('again', cb);
// 以上是新添加代码
</script>
</body>
</html>

看执行结果吧!


一͛世͛珍͛藏͛
86 声望6 粉丝

« 上一篇
Let's Encrypt