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. java 数组声明方法

    //数组 public class Test16{ public static void main(String args[]){ //声明一: int [] x; x = new int[3];// ...

  2. jQuery live事件说明及移除live事件方法

    1.live事件说明 jQuery1.3增加了一个live()方法,下面是手册上的说明: jQuery 1.3中新增的方法.给所有当前以及将来会匹配的元素绑定一个事件处理函数(比如click事件).也 ...

  3. jenkins里面使用批处理命令进行自动部署

    http://blog.csdn.net/hwhua1986/article/details/47974047

  4. Win一键查看用户密码

    测试Win7环境,能探测系统密码 Download: 链接: https://pan.baidu.com/s/1skTeNrJ 密码: avpa

  5. Windows开机BIOS启动快捷按键

    Window装系统,开机需要调整启动选项...不同的机型,进入BIOS按键不同 按键列表

  6. 使用badblocks检测坏块

    命令格式 badblocks [-svw][-b <区块大小>][-o <输出文件>][磁盘装置][磁盘区块数 [启始区块]] 典型的命令如下 # 写测试, 数据安全 -c - ...

  7. 非常漂亮滴皮肤skin++ 终极破解之法

    破解includeparametershook汇编windows *[标题]:Skin++通用界面换肤系统V2.0.1破解探讨 *[作者]:gz1X <gz1x(at)tom(dot)com&g ...

  8. Arduino和C51开发光敏传感器

    技术:51单片机.Arduino.光敏传感器.PCF8591.AD/DA转换   概述 本文介绍了如何接收传感器的模拟信号和如何使用PCF8591 AD/DA转换模块对光敏传感器的模拟信号进行转换.讲 ...

  9. Android短信管家视频播放器代码备份

    自己保留备份,增强记忆   这是video的类 public class VideoActivity extends Activity { /** * 解析网络页面 */ private WebVie ...

  10. Redis 学习之路 (009) - Redis-cli命令最新总结

    资料来源: http://redisdoc.com/ http://redis.io/commands 连接操作相关的命令 默认直接连接  远程连接-h 192.168.1.20 -p 6379 pi ...