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. Android API之Telephony.Threads

    1. Telephony.ThreadsColumns Columns for the "threads" table used by MMS and SMS. 在Telephon ...

  2. (原)tensorflow中finetune某些层

    转载请注明处处: http://www.cnblogs.com/darkknightzh/p/7608709.html 参考网址: https://kratzert.github.io/2017/02 ...

  3. JavaScript 数组(Array)对象

    Array 对象 Array 对象用于在单个的变量中存储多个值. 创建 Array 对象的语法: new Array(); new Array(size); new Array(element0, e ...

  4. linux线程学习

    按照书上写的,不知道为什么有问题: //已解决,参考最新的blog,哈哈 #include <stdlib.h> #include <pthread.h> #include & ...

  5. sid-msg.map文件概述

    我这边编写了magic对应的指定文件规则,但是运行的时候发现储存的文件中包含我未指定的数据文件: 在rules下边看的时候,发现有sid-msg.map文件,上网了解下这个文件是干啥的.. 下边文章来 ...

  6. Apache Rewrite规则详解[转]

    1.Rewrite规则简介:Rewirte主要的功能就是实现URL的跳转,它的正则表达式是基于Perl语言.可基于服务器级的(httpd.conf)和目录级的(.htaccess)两种方式.如果要想用 ...

  7. iostat 监视I/O子系统

    iostat是I/O statistics(输入/输出统计)的缩写,用来动态监视系统的磁盘操作活动. 11.1. 命令格式 iostat[参数][时间][次数] 11.2. 命令功能 通过iostat ...

  8. C语言终极面试及答案分析

    第一部分:基本概念及其它问答题 .关键字static的作用是什么? 这个简单的问题很少有人能回答完全.在C语言中,关键字static有三个明显的作用: ). 在函数体,一个被声明为静态的变量在这一函数 ...

  9. 【MyBatis】MyBatis之如何配置

    1,MyBatis简介 MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索.MyBatis 使用简 ...

  10. 【struts2】Action的生命周期

    Struts2的Action的生命周期是:Struts2为每个请求都重新初始化一个Action的实例.可以稍微改造一下代码来验证一下. 给HelloWorldAction加上一个public无参的构造 ...