ES基础知识七(async函数、Iterator和for...of循环)

一、async 函数

1、基本用法

async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

async function getStockPriceByName(name) {
  const symbol = await getStockSymbol(name);
  const stockPrice = await getStockPrice(symbol);
  return stockPrice;
}

getStockPriceByName('goog').then(function (result) {
  console.log(result);
});

2、语法

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('resolve', v)
).catch(
	e => console.log('reject', e)
)
//reject Error: 出错了

正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。下面是一个简化的sleep休眠实例。

function sleep(interval) {
  return new Promise(resolve => {
    setTimeout(resolve, interval);
  })
}

// 用法
async function one2FiveInAsync() {
  for(let i = 1; i <= 5; i++) {
    console.log(i);
    await sleep(1000);
  }
}

one2FiveInAsync();

任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。

async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}

如果希望即使前一个异步操作失败,也不要中断后面的异步操作,可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。或者,await后面的 Promise 对象再跟一个catch方法,处理前面可能出现的错误。

async function f() {
  await Promise.reject('出错了')
    .catch(e => console.log(e));
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))
// 出错了
// hello world

async function f() {
  try {
    await Promise.reject('出错了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))
// hello world

多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

// 继发执行
let foo = await getFoo();
let bar = await getBar();

// 并发执行
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

await命令只能用在async函数之中,如果用在普通函数,就会报错。

二、Iterator 和 for…of 循环

1、Iterator遍历器

遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制(即for...of循环)。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

原生具备 Iterator 接口的数据结构如下。

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

变量arr是一个数组,原生就具有遍历器接口,部署在arrSymbol.iterator属性上面。所以,调用这个属性,就得到遍历器对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

对象(Object)之所以没有默认部署 Iterator 接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。

2、for…of循环

一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。

JavaScript 原有的for...in循环,只能获得对象的键名,不能直接获取键值。ES6 提供for...of循环,允许遍历获得键值。

var arr = ['a', 'b', 'c', 'd'];

for (let a in arr) {
  console.log(a); // 0 1 2 3
}

for (let a of arr) {
  console.log(a); // a b c d
}

for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟for...in循环也不一样。

let arr = [3, 5, 7];
arr.foo = 'hello';

for (let i in arr) {
  console.log(i); // "0", "1", "2", "foo"
}

for (let i of arr) {
  console.log(i); //  "3", "5", "7"
}

上面代码中,for...of循环不会返回数组arrfoo属性。

SetMap 结构也原生具有 Iterator 接口,可以直接使用for...of循环。

var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
  console.log(e);
}
// Gecko
// Trident
// Webkit

var es6 = new Map();
es6.set("edition", 6);
es6.set("committee", "TC39");
es6.set("standard", "ECMA-262");
for (var [name, value] of es6) {
  console.log(name + ": " + value);
}
// edition: 6
// committee: TC39
// standard: ECMA-262

let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
  console.log(pair);
}
// ['a', 1]
// ['b', 2]

上面代码演示了如何遍历 Set 结构和 Map 结构。值得注意的地方有两个,首先,遍历的顺序是按照各个成员被添加进数据结构的顺序。其次,Set 结构遍历时,返回的是一个值,而 Map 结构遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员的键名和键值。

for...in循环有几个缺点:

  • 数组的键名是数字,但是for...in循环是以字符串作为键名“0”、“1”、“2”等等。
  • for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
  • 某些情况下,for...in循环会以任意顺序遍历键名。

总之,for...in循环主要是为遍历对象而设计的,不适用于遍历数组。

for...of循环相比上面几种做法,有一些显著的优点:

  • 有着同for...in一样的简洁语法,但是没有for...in那些缺点。
  • 不同于forEach方法,它可以与breakcontinuereturn配合使用。
  • 提供了遍历所有数据结构的统一操作接口。
for (var n of fibonacci) {
  if (n > 1000)
    break;
  console.log(n);
}

#参考文档
ES6入门教程(阮一峰)-- async 函数、Iterator 和 for…of 循环

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值