HTML5学习笔记(十九):Lambda和Promise
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的更多相关文章
- python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法
python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法 同一台机器同时安装 python2.7 和 python3.4不会冲突.安装在不同目录,然 ...
- (C/C++学习笔记) 十九. 模板
十九. 模板 ● 模板的基本概念 模板(template) 函数模板:可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体设计. 语法: template <<模 ...
- Java基础学习笔记十九 IO
File IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再 ...
- Java基础学习笔记十九 File
IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再把这些数据 ...
- HTML5学习笔记(九):选择器详解
在前面的笔记中我们已经接触过几种常见的选择器,本笔记我们将深入了解CSS的选择器. 元素选择器 最常见的 CSS 选择器是元素选择器.换句话说,文档的元素就是最基本的选择器.在 W3C 标准中,元素选 ...
- angular学习笔记(十九)-指令修改dom
本篇主要介绍angular使用指令修改DOM: 使用angular指令可以自己扩展html语法,还可以做很多自定义的事情.在后面会专门讲解这一块的知识,这一篇只是起到了解入门的作用. 与控制器,过滤器 ...
- JavaScript权威设计--跨域,XMLHttpRequest(简要学习笔记十九)
1.跨域指的是什么? URL 说明 是否允许通信 http://www.a.com/a.jshttp://www.a.com/b.js 同一域名下 允许 http://www.a.com/lab/a. ...
- python 学习笔记十九 django深入学习四 cookie,session
缓存 一个动态网站的基本权衡点就是,它是动态的. 每次用户请求一个页面,Web服务器将进行所有涵盖数据库查询到模版渲染到业务逻辑的请求,用来创建浏览者需要的页面.当程序访问量大时,耗时必然会更加明显, ...
- SharpGL学习笔记(十九) 摄像机漫游
所谓的摄像机漫游,就是可以在场景中来回走动. 现实中,我们通过眼睛观察东西,身体移动带动眼睛移动观察身边的事物,这也是在漫游. 在OpenGL中我们使用函数LookAt()来操作摄像机在三维场景中进行 ...
- yii2源码学习笔记(十九)
view剩余代码 /** * @return string|boolean the view file currently being rendered. False if no view file ...
随机推荐
- Android 自定义控件-高仿猎豹清理大师自定义内存开口圆环控件
技术:Android+java 概述 看见猎豹清理大师的内存开口圆环比例进度 挺有意思的,于是就是想自己实现下这样的效果,于是反编译了猎豹清理 大师的app看了下,原来是有两张图,于是脑子里就过了 ...
- 持续集成工具jenkins的使用
jenkins类似于Hadson,是一款持续集成工具.使用jenkins完成自动化部署的表现为:当开发人员向版本库提交新的代码后,应用服务器上自动部署,用户或测试人员使用的马上就是最新的应用程序.搭建 ...
- ios中第三方库归结
1:uiscrollview 折叠 展开中不包含tablecell. 展开列表效果 Collapse Click () https://github.com/bennyguitar/Collapse ...
- NoSQL生态系统(nosql ecosystem)
Unlike most of the other projects in this book, NoSQL is not a tool, but an ecosystem composed of se ...
- [Spring学习笔记 2 ]装配各种类型的属性 map,list,array,null,properties
一.spring Ioc容器补充(1) Spring Ioc容器 DI(依赖注入): 注入的方式:设值方法注入setter(属性注入)/构造子注入(构造函数传入依赖的对象)/字段注入field(注解) ...
- Linux主要shell命令详解(中)
shell中的特殊字符 shell中除使用普通字符外,还可以使用一些具有特殊含义和功能的特殊字符.在使用它们时应注意其特殊的含义和作用范围.下面分别对这些特殊字符加以介绍. 1. 通配符 通配符用于模 ...
- k8s实战之数据卷(volume)
一.概述 数据卷用于实现容器持久化数据,k8s对于数据卷重新定义,提供了丰富强大的功能:数据卷分为三类: 本地数据卷,网络数据卷和信息数据卷 二.
- 【Spring】SpringMVC之详解AOP
1,AOP简介 Aspect Oriented Programming 面向切面编程.AOP还是以OOP为基础,只不过将共同逻辑封装为组件,然后通过配置的方式将组件动态切入到原有组件中.这样做的有点 ...
- 【JavaScript】浅析ajax的使用
目录结构: contents structure [+] Ajax简介 Ajax的工作原理 Ajax的使用步骤 使用原生的js代码 使用JQuery代码 JQuery中常用的Ajax函数 $.ajax ...
- iOS UTI
UTI全拼Uniform Type Identifier,直接翻译过来就是统一类型标示符. 这个东西可以实现的功能就是设备之间或者app之间的文件传输. 有兴趣的领个支付宝红包再走, ^_^ 全套相关 ...