Lambda

在ES6的标准中称为Arrow Function(箭头函数)。下面是一个简单的箭头函数:

x => x * x

上面的定义和下面的代码定义效果一样:

 function (x) {
return x * x;
}

箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种像上面的,只包含一个表达式,连{ ... }和return都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ ... }和return:

 x => {
if (x > 0) {
return x * x;
}
else {
return - x * x;
}
}

如果参数不是一个,就需要用括号()括起来:

 // 两个参数:
(x, y) => x * x + y * y // 无参数:
() => 3.14 // 可变参数:
(x, y, ...rest) => {
var i, sum = x + y;
for (i=0; i<rest.length; i++) {
sum += rest[i];
}
return sum;
}

如果要返回一个对象,就要注意,如果是单表达式,这么写的话会报错:

x => { foo: x }

因为和函数体的{ ... }有语法冲突,所以要改为:

x => ({ foo: x })

this

箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。

回顾前面的例子,由于JavaScript函数对this绑定的错误处理,下面的例子无法得到预期结果:

 var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; //
var fn = function () {
return new Date().getFullYear() - this.birth; // this指向window或undefined
};
return fn();
}
};

现在,箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj

 var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; //
var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
return fn();
}
};
obj.getAge(); //

如果使用箭头函数,以前的那种hack写法:

var that = this;

就不再需要了。

由于this在箭头函数中已经按照词法作用域绑定了,所以,用call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略:

 var obj = {
birth: 1990,
getAge: function (year) {
var b = this.birth; //
var fn = (y) => y - this.birth; // this.birth仍是1990
return fn.call({birth:2000}, year);
}
};
obj.getAge(2015); //

而Lambda则是多种语言中对箭头函数的通用叫法。

Promise

在JavaScript的世界中,所有代码都是单线程执行的。

由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。

所以在编写异步操作时,一般都是使用回调或观察者模式来订阅事件,那么就会出现一种问题,当我的一个异步是基于另一个异步之后的,就会出现嵌套的代码,我们来看一个例子。

这里我们设计一个抽奖程序,用户需要连抽3次奖,每次都中才能得到大奖,有一次失败则全部失败,抽奖采用异步请求(这里我们就用setTimeout和随机数函数代替异步的网络请求),其中,第一次抽奖概率90%,第二次70%,最后一次50%但是请求的方式会变(即不能复用前两次的代码)。前两次使用func1来模拟,最后一次用func2来模拟,最后的代码如下:

 // 假设异步调用会在 0.5 秒后执行 value 异步调用的成功率 0 - 1,如果成功调用 success,如果失败调用 fail
function func1(value, success, fail) {
setTimeout(function() {
var r = Math.random();
if(value > r) {
success();
} else {
fail();
}
}, 500);
} // 和上面的函数功能一致,只是成功率为 50%
function func2(success, fail) {
setTimeout(function() {
var r = Math.random();
if(0.5 > r) {
success();
} else {
fail();
}
}, 1000);
} func1(0.9, function() {
console.log("第1次90%成功概率,成功了,执行下一次调用"); func1(0.7, function() {
console.log("第2次70%成功概率,成功了,执行下一次调用"); func2(function() {
console.log("第3次50%成功概率,成功了,你运气真好,赢得了奖励");
}, function() {
console.log("第3次50%成功概率,失败了,好可惜");
}); }, function() {
console.log("第2次70%成功概率,失败了,调用结束");
}); }, function() {
console.log("第1次90%成功概率,失败了,调用结束");
});

可以通过刷新网页来多次查看抽奖的结果。

那么我们可以发现,代码中出现了3次嵌套,如果我们的抽奖要连续抽10次,就会出现10个嵌套了。

初识Promise

Promise有各种开源实现,在ES6中被统一规范,由浏览器直接支持。

下面给出ES6之前的开源项目列表,如果需要在ES6之前版本的浏览器上运行可以用到:

我们这篇笔记基于ES6规范的Promise来学习。

Promise用来干嘛的?你猜的没错,就是用来解决我们上面的异步代码编写出现嵌套的问题。

Promise最核心的技术就是提供一种类似同步代码的编写方式来编写异步代码,从而避免异步调用的嵌套导致的问题。

我们先看一看上面例子中的func2(不带参数)用Promise怎么写:

 function func(resolve, reject) {
setTimeout(function() {
var r = Math.random();
if(0.5 > r) {
resolve("success");
} else {
reject("fail");
}
}, 1000);
} var p1 = new Promise(func);
var p2 = p1.then(function (result) {
console.log('成功:' + result);
});
var p3 = p2.catch(function (reason) {
console.log('失败:' + reason);
}); console.log(p1 === p2); // false
console.log(p1 === p3); // false
console.log(p2 === p3); // false

这个func()函数有两个参数,这两个参数都是函数,如果执行成功,我们将调用resolve,如果执行失败,我们将调用reject。可以看出,func()函数只关心自身的逻辑,并不关心具体的resolve和reject将如何处理结果。

我们创建的Promise会执行传入的函数,当调用then时传入的函数会在func函数中调用resolve时被调用,同时可以拿到传递的参数,同样catch对应的是reject函数。

另外我们可以发现,then和catch都会返回一个全新创建的Promise对象,这样我们可以采用链式调用来写:

 new Promise(func)
.then(function (result) {
console.log('成功:' + result);
})
.catch(function (reason) {
console.log('失败:' + reason);
});

Promise对象接受的就是这样的一个函数,没有返回值,那么如果需要传递参数该怎么办?下面再看看func1(带有参数)的Promise怎么写:

 function func(value) {
return function(resolve, reject) {
setTimeout(function() {
var r = Math.random();
if(value > r) {
resolve("success");
} else {
reject("fail");
}
}, 500);
};
} new Promise(func(0.9))
.then(function (result) {
console.log('成功:' + result);
})
.catch(function (reason) {
console.log('失败:' + reason);
});

我们发现使用闭包的方式,定义一个函数返回给Promise使用的函数即可实现参数的传递。

串行执行异步

串行执行即一个异步执行完毕后开始执行下一个,类似我们最开始的3重嵌套例子,使用Promise的实现如下:

 // 假设异步调用会在 0.5 秒后执行 value 异步调用的成功率 0 - 1
function func1(value) {
return function(resolve, reject) {
setTimeout(function() {
var r = Math.random();
if(value > r) {
resolve("success");
} else {
reject("fail");
}
}, 500);
};
} // 和上面的函数功能一致,只是成功率为 50%
function func2() {
return function(resolve, reject) {
setTimeout(function() {
var r = Math.random();
if(0.5 > r) {
resolve("success");
} else {
reject("fail");
}
}, 1000);
};
} new Promise(func1(0.9))
.then(function (result) {
console.log("第1次90%成功概率,成功了,执行下一次调用");
// 返回新的 Promise 对象继续执行
return new Promise(func1(0.7));
}).then(function (result) {
console.log("第2次70%成功概率,成功了,执行下一次调用");
// 返回新的 Promise 对象继续执行
return new Promise(func2());
})
.then(function (result) {
console.log("第3次50%成功概率,成功了,你运气真好,赢得了奖励");
// 全部执行完毕
})
.catch(function (reason) {
// 可以将第几次抽奖或其它信息通过 reason 参数传递过来
console.log("失败了,好可惜");
});

如你所见,我们使用了类似同步的代码实现了异步的逻辑执行。

并行执行异步

试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all()实现如下:

 var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
console.log(results); // 获得一个Array: ['P1', 'P2']
});

有些时候,多个异步任务是为了容错。比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现:

var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
console.log(result); // 'P1'
});

由于p1执行较快,Promise的then()将获得结果'P1'。p2仍在继续执行,但执行结果将被丢弃。

如果我们组合使用Promise,就可以把很多异步任务以并行和串行的方式组合起来执行。

HTML5学习笔记(十九):Lambda和Promise的更多相关文章

  1. python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法

    python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法 同一台机器同时安装 python2.7 和 python3.4不会冲突.安装在不同目录,然 ...

  2. (C/C++学习笔记) 十九. 模板

    十九. 模板 ● 模板的基本概念 模板(template) 函数模板:可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体设计. 语法: template <<模 ...

  3. Java基础学习笔记十九 IO

    File IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再 ...

  4. Java基础学习笔记十九 File

    IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再把这些数据 ...

  5. HTML5学习笔记(九):选择器详解

    在前面的笔记中我们已经接触过几种常见的选择器,本笔记我们将深入了解CSS的选择器. 元素选择器 最常见的 CSS 选择器是元素选择器.换句话说,文档的元素就是最基本的选择器.在 W3C 标准中,元素选 ...

  6. angular学习笔记(十九)-指令修改dom

    本篇主要介绍angular使用指令修改DOM: 使用angular指令可以自己扩展html语法,还可以做很多自定义的事情.在后面会专门讲解这一块的知识,这一篇只是起到了解入门的作用. 与控制器,过滤器 ...

  7. JavaScript权威设计--跨域,XMLHttpRequest(简要学习笔记十九)

    1.跨域指的是什么? URL 说明 是否允许通信 http://www.a.com/a.jshttp://www.a.com/b.js 同一域名下 允许 http://www.a.com/lab/a. ...

  8. python 学习笔记十九 django深入学习四 cookie,session

    缓存 一个动态网站的基本权衡点就是,它是动态的. 每次用户请求一个页面,Web服务器将进行所有涵盖数据库查询到模版渲染到业务逻辑的请求,用来创建浏览者需要的页面.当程序访问量大时,耗时必然会更加明显, ...

  9. SharpGL学习笔记(十九) 摄像机漫游

    所谓的摄像机漫游,就是可以在场景中来回走动. 现实中,我们通过眼睛观察东西,身体移动带动眼睛移动观察身边的事物,这也是在漫游. 在OpenGL中我们使用函数LookAt()来操作摄像机在三维场景中进行 ...

  10. yii2源码学习笔记(十九)

    view剩余代码 /** * @return string|boolean the view file currently being rendered. False if no view file ...

随机推荐

  1. 使用perf + FlameGraph生成进程火焰图

    FlameGraph代码:https://github.com/cobblau/FlameGraph 使用方法 1,perf record --call-graph dwarf -p 12345 2, ...

  2. java日期格式(年月日时分秒毫秒)

    package test.remote.tools.combine; import java.text.SimpleDateFormat; import java.util.Calendar; imp ...

  3. socket.io简介

    websocket是一种比较简单的协议,各种语言中都有很多实现版本,实际上它们差别不大,都是在websocket的基础上做些封装,随便选一个即可. socket.io就是众多websocket库中的一 ...

  4. MATLAB 不能保存变量问题及解决办法

    在使用matlab保存结构体.元胞数组等等的变量时,matlab总是提示 警告: 未保存变量 'session'.对于大于 2GB 的变量,请使用 MAT 文件版本 7.3 或更高版本.  问题如下: ...

  5. property相关参数介绍

    1.copy 使用类型 :NSString,block 2.assign使用类型 :delegate,int,float,NSInteger,bool,枚举,结构体... 3.retain使用类型 : ...

  6. iOS 10 的一个重要更新-开发 iMessage 的第三方插件

    苹果官方的 Messages 在 iOS 10 推出了非常重大的更新,可能主要是想从其他 IM 巨头手里抢点市场份额回来,包括 Facebook Messenger, Wechat 和 Snapcha ...

  7. libev4.15学习

    libev作为优秀的高性能IO框架,非常值得学习! 虽然我是菜鸟,但也必须学习啦,从今天一点一点地学习,慢慢进步! # include "ev.h" struct event_ba ...

  8. 【Struts2】剖析Struts2中的反射技术 ValueStack(值栈)

    1,Struts2框架主要组件的处理流程 在说ValueStack之前,笔者先说一说Struts2中常用的组件,struts2中常用组件有strutsPrepareAndExecuteExceptio ...

  9. JSON-handle-好玩的google插件

    一:前言 现在的主要工作是写后台的接口服务,而JSON这种数据格式的数据是最为常用的,经常使用在线的格式化工具,来检查或者编写对应的数据,感觉不够智能,于是试验了一下几个插件,感觉有些挺好用的,在此记 ...

  10. libevent的问题

    问题: nginx error while loading shared libraries: libevent-2.0.so.5: cannot open shared object file: N ...