【ES8】async 函数

相关介绍

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。async 函数是什么?一句话,它就是 Generator 函数的语法糖。通过比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已

四大特点

  1. 内置执行器
async 函数自带执行器。也就是说 async 函数的执行,与普通函数一模一样,只要一行
asyncReadFile();
  1. 更好的语义
asyncawait,比起星号和yield,语义更清楚了
async:表示函数里有异步操作
await:表示紧跟在后面的表达式需要等待结果
  1. 更好的语义
async函数的await命令后面,可以是 Promise 对象和原始类型的值
(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)
  1. 返回值是 Promise
async函数的返回值是 Promise 对象,你可以用then方法指定下一步的操作。
async函数完全可以看作多个异步操作,包装成的一个 Promise 对象
而await命令就是内部 then 命令的语法糖

基本使用

  • async 函数使用形式:
// 函数声明
async function foo() {}

// 函数表达式
const foo = async function () {};

// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)

// Class 的方法
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}

const storage = new Storage();
storage.getAvatar('jake').then();

// 箭头函数
const foo = async () => {};
  • 语法:
async function fn(){
	const res = await promise对象;
}
fn().then(function (result) {
  console.log("async/await");
});
  • 分析:
- 函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象
- async函数返回一个 Promise 对象,可以使用then方法添加回调函数
- 当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
  • 必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变
// 案例:指定多少毫秒后输出一个值
function timeout(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}
// 指定 50 毫秒以后,输出hello world
asyncPrint('hello world', 50);
  • async函数返回的是 Promise 对象,可以作为await命令的参数
async function timeout(ms) {
  await new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 50);
  • async函数内部return语句返回的值,会成为then方法回调函数的参数
async function f() {
  return 'hello world';
}
// 函数f内部return命令返回的值,会被then方法回调函数接收到
f().then(v => console.log(v))
// "hello world"
  • 只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
function timeout(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 1000).then(()=>{
	console.log("async/await");
});
// "hello world"
// "async/await"
  • await 命令后面是定义了then方法的对象,那么await会将其等同于 Promise 对象
class Sleep {
  constructor(timeout) {
    this.timeout = timeout;
  }
  // 但是因为定义了then方法,await会将其视为Promise处理
  then(resolve, reject) {
    const startTime = Date.now();
    setTimeout(
      () => resolve(Date.now() - startTime),
      this.timeout
    );
  }
}

(async () => {
  // await命令后面是一个Sleep对象的实例,这个实例不是 Promise 对象
  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语句是不会执行的,因为第一个await语句状态变成了reject
async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}

  • await放在try…catch结构里面
// 使前一个异步操作失败,也不要中断后面的异步操作
async function f() {
  try {
    // 不管这个异步操作是否成功,第二个await都会执行
    await Promise.reject('出错了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

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

注意事项

  • 最好把await命令放在try...catch代码块中
async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// 另一种写法

async function myFunction() {
  await somethingThatReturnsAPromise()
  .catch(function (err) {
    console.log(err);
  });
}
  • 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发
// 只有getFoo完成以后,才会执行getBar
let foo = await getFoo();
let bar = await getBar();

// 同时触发写法 1
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 同时触发写法 2
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
  • await命令只能用在async函数之中,如果用在普通函数,就会报错
async function dbFuc(db) {
  let docs = [{}, {}, {}];

  // 报错
  docs.forEach(function (doc) {
    await db.post(doc);
  });
}
  • async 函数可以保留运行堆栈
/*
	1. 函数a内部运行了一个异步任务b()。
	2. 当b()运行的时候,函数a()不会中断,而是继续执行。
	3. 等到b()运行结束,可能a()早就运行结束了,b()所在的上下文环境已经消失了。
	4. 如果b()或c()报错,错误堆栈将不包括a()
*/
const a = () => {
  b().then(() => c());
};


/*
	1. b()运行的时候,a()是暂停执行,上下文环境都保存着
	2. 一旦b()或c()报错,错误堆栈将包括a()
*/
const a = async () => {
  await b();
  c();
};

同类比较

假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值

  • Promise 的写法:
function chainAnimationsPromise(elem, animations) {

  // 变量ret用来保存上一个动画的返回值
  let ret = null;

  // 新建一个空的Promise
  let p = Promise.resolve();

  // 使用then方法,添加所有动画
  for(let anim of animations) {
    p = p.then(function(val) {
      ret = val;
      return anim(elem);
    });
  }

  // 返回一个部署了错误捕捉机制的Promise
  return p.catch(function(e) {
    /* 忽略错误,继续执行 */
  }).then(function() {
    return ret;
  });

}
  • Generator 函数的写法:
function chainAnimationsGenerator(elem, animations) {

  return spawn(function*() {
    let ret = null;
    try {
      for(let anim of animations) {
        ret = yield anim(elem);
      }
    } catch(e) {
      /* 忽略错误,继续执行 */
    }
    return ret;
  });

}
  • async 函数的写法
async function chainAnimationsAsync(elem, animations) {
  let ret = null;
  try {
    for(let anim of animations) {
      ret = await anim(elem);
    }
  } catch(e) {
    /* 忽略错误,继续执行 */
  }
  return ret;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一颗不甘坠落的流星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值