一.Promise对象
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
p的状态由p1、p2、p3决定,分成两种情况。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
下面是一个具体的例子。
// 生成一个Promise对象的数组const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
return getJSON('/post/' + id + ".json");});
Promise.all(promises).then(function (posts) {
// ...}).catch(function(reason){
// ...});
上面代码中,promises是包含 6 个 Promise 实例的数组,只有这 6 个实例的 状态都变成fulfilled,或者其中有一个变为rejected,才会调用Promise.all方法后 面的回调函数。
二.Async函数:使得异步操作更加方便
async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
Async和await命令后面,可以是promise对象和原始类型的值(数值,字符串和布尔值,但这时会自动转成立即resolved的promise对象)。
async函数的返回值是promise对象,可以用then方法指定下一步的操作,进一步说,async函数完全可以看作多个异步操作,包装成一个promise对象,而await命令就是内部then命令的语法糖。
1.基本语法
async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
async函数返回一个 Promise 对象,async函数内部return语句返回的值,会成为then方法回调函数的参数。
async function f() {
return 'hello world';}
f().then(v => console.log(v)) // "hello world"
async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。
async function f() {
throw new Error('出错了');}
f().then(
v => console.log(v),
e => console.log(e)) // Error: 出错了
async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
async function f() {
// 等同于 return 123;
return await 123;
}
f().then(v => console.log(v)) // 123
另一种情况是,await命令后面是一个thenable对象(即定义了then方法的对象),那么await会将其等同于 Promise 对象。
class Sleep {
constructor(timeout) {
this.timeout = timeout;
}
then(resolve, reject) {
const startTime = Date.now();
setTimeout(
() => resolve(Date.now() - startTime),
this.timeout
);
}}
(async () => {
const sleepTime = await new Sleep(1000);
console.log(sleepTime);})();
// 1000
await命令后面的 Promise 对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。
async function f() {
await Promise.reject('出错了');}
f().then(v => console.log(v)).catch(e => console.log(e)) // 出错了
任何一个await语句后面的promise对象变为reject状态,那么整个async函数都会中断执行。
有时我们希望即使前一个异步操作失败,也不要中断后面的异步操作,这时可以将第一个await放在try…catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。
另一种方法是await后面的promise对象在跟一个catch方法,处理前面可能出现的错误。
多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
let foo = await getFoo();
let bar = await getBar();
上面代码中,getFoo和getBar是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo完成以后,才会执行getBar,完全可以让它们同时触发。
await命令只能用在async函数之中,如果用在普通函数,就会报错。
如果确实希望多个请求并发执行,可以使用Promise.all方法。当三个请求都会resolved时,下面两种写法效果相同。
三.class的基本语法
Es6可以看作只是一个语法糖,它的绝大部分功能,es5都可以做到,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程的语法而已。
Class Point{ //定义一个类
Constructor(x, y){ //类的构造函数
This.x = x; //this代表实例对象
This.y = y;
}
toString(){ //类中的方法
Return ‘(’+ this.x + ‘, ’+ this.y + ‘)’;
}
}
Class ColorPoint extends Point{ //子类继承父类:extends关键字实现类的继承
Constructor(x,y,color){
Super(x, y); //super作为函数调用,代表父类构造函数,只能放在子类构造函数中,且只能放在第一句,但是返回的是子类的实例,因此super相当于Point.prototype.constructor.call(this)
This.color = color;
}
toString() {
Return this.color + ‘ ’ + super.toString(); //super当作一个对象来使用,这时super在普通方法之中,指向Point.prototype, 所以super.toString()就相当于Point.prototype.toString()。由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。
}
}
Let colorPoint1 = new Point(3,4); //类的实例对象
四.module的语法
Es6模块: import { stat, exits, readFile} from ‘fs’;
Es6模块自动采用严格模式,不管你有没有在模块头部加上”use strict”
模块功能主要由两个命令构成: export和import。 export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
- 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。下面是一个 JS 文件,里面使用export命令输出变量。
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
上面的写法可以写成(推荐使用这种2方法):
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };
Export除了可以输出变量,还可以输出类或者函数
Export function multiply(x, y){ return x*y; };
通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名:
Function v1() {......}
Function v2() {......}
Export { v1 as v11, v2 as v22, v2 as v33};
上面代码使用as关键字,重命名了函数v1和v2的对外接口,重命名后,v2可以用不同的名字输出两次,注意写法规范,否则会报错,下面是正确的写法:
//写法一: export var m = 1;
//写法二: var m = 1; export {m};
//写法三: var n= 1; export {n as m};
//导出函数写法一:export function f() {};
//导出函数写法二: function f() {} export {f};
export命令可以出现在模块的任何位置,只要处于模块顶层就可以,如果处于块级作用域内,就会报错,import命令也是如此。
2.使用export命令定义了模块的对外接口外,其他js文件就可以通过import命令加载这个模块。
Import { firstname, lastname, year } from ‘./profile.js’;
Function setname(ele) {
Ele.textContent = firstname+ ‘ ’+lastname;
}
Import大括号里面的变量名,必须与被导入模块profile.js对外接口的名称相同。要想为输入的变量重新取一个名字,import命令要用as关键字,将输入的变量重命名。
Import { lastname as name } from ‘./profile.js’
不能对import加载过来的变量重新赋值,但是如果加载过来的是个对象,则可采用对象名.属性名的方式改写。
From指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js后缀可以省略。
import命令具有提升效果,会提升到整个模块的头部,首先执行。
import是静态执行,所以不能使用表达式和变量,也不能写在if这些块级作用域中。
import { foo } from 'my_module';
import { bar } from 'my_module';
// 等同于import { foo, bar } from 'my_module';
3.从前面的例子可以看出,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。
为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。
// export-default.js
export default function () {
console.log('foo');
}
上面代码是一个模块文件export-default.js,它的默认输出是一个函数。
其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。
// import-default.js
import customName from './export-default';
customName(); // 'foo'
上面代码的import命令,可以用任意名称指向export-default.js输出的方法,这时就不需要知道原模块输出的函数名。需要注意的是,这时import命令后面,不使用大括号。
下面比较一下默认输出和正常输出。
// 第一组
export default function crc32() { // 输出 // ...}
import crc32 from 'crc32'; // 输入
// 第二组
export function crc32() { // 输出 // ...};
import {crc32} from 'crc32'; // 输入
上面代码的两组写法,第一组是使用export default时,对应的import语句不需要使用大括号;第二组是不使用export default时,对应的import语句需要使用大括号。
export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。
正是因为export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句。
// 正确 export var a = 1;
// 正确 var a = 1; export default a;
// 错误 export default var a = 1;
4.export和import的复合写法
如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。
export { foo, bar } from 'my_module';
// 可以简单理解为import { foo, bar } from 'my_module';
export { foo, bar };
具名接口改为默认接口的写法如下。
export { es6 as default } from './someModule';
// 等同于
import { es6 } from './someModule';
export default es6;