generator和async-await用法介绍

88 阅读3分钟
1. generator
  • generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数(但并不是一个函数),但可以返回多次;
  • generator由function定义(注意多出的号),并且,除了return语句,还可以用yield返回多次;
  • next()方法会执行generator的代码,然后,每次遇到yield 语句;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。如果done为true,则value就是return的返回值;
// 生成器的声明
function* gen() {
  console.log('abc');
  console.log('1');
  console.log('2');
  console.log('3');
  // 礼让 => 异步
  let res = yield 'Green';
  //下面打印的res就是yield返回的'Green'
  console.log('444', res) // 444 Green
  console.log('555')
}

// 获取生成器对象
let generator = gen();
let res = generator.next();
console.log(res); // {value: 'Green', done: false}
generator.next(res.value);
2. async-await(ES2017)
  • async-await是ES2017(ES8)提出的基于Promise的解决异步的最终方案,现在更为常用;
  • async是一个加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象resolve的值。因此对async函数可以直接then,返回值就是then方法传入的函数。
  • await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待;
  • await 修饰的如果是Promise对象:可以获取Promise中返回的内容(resolve或reject的参数),且取到值后语句才会往下执行;
  • 如果不是Promise对象:把这个非promise的东西当做await表达式的结果;
// 看似同步代码
async function asyncFn() {
  console.log('3');
  // await可以控制异步
  let res = await new Promise(res => {
    console.log('5');
    res('数据abc');
  })
  // 不调用res('数据abc'); 则不执行后续代码
  // 等同于.then里面的代码
  console.log('4', res); // 也就是说不调用res('数据abc'),本句打印代码不会执行
}
// 同步优先
console.log('1')
asyncFn();
2.1 async-await应用--处理没有try的reject异常

原理是参数错误优先规则,自定义一个pretty函数来对promise对象进行预处理

function pretty(promise) {
  // 中间层处理 , 参数错误优先
  return promise.then(res => {
    return [undefined, res];
  })// 处理过以后fulfilled
    .catch(err => {
      return [err, undefined];
    })// 处理过以后fulfilled
}

使用场景,处理没有try的reject异常

const { log: l } = console;

function req() {       // 去解决,拒绝
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 数据回来了
      let data = Math.random();
      if (data < 0.5) return reject(data)
      else return resolve(data); // 调用20行的参数,函数
    }, 1000);
  })
}

// 在run函数使用自定义pretty函数,并配合async-await实现功能
async function run() {
  let [err, data] = await pretty(req());
  if (err) return console.log('错误了', err)
  console.log('成功,数据是:', data)

  var res = await req()
  l('成功')
}

 run(); // 调用run函数进行测试

也可以使用以下方法处理没有try的reject异常

window.addEventListener('unhandledrejection', (err) => {
  err.preventDefault();
  // reject(参数)
  console.log(err.reason, '错误了');
});
3. async-await在forEach循环里的应用

使用场景,想要实现每隔1s调用一次函数fn

let arr = [1, 2, 3, 4, { name: 'Green', fn() { } }]
const { log: l } = console;
function req(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(n);
    }, 1000);
  })
}
arr.forEach(async (ele, index, newArr) => {
  await req(ele);
  console.log('响应了...', ele, newArr)
})

结果只等待1s就全部响应完毕

重写forEach进行优化

// 覆盖forEach方法
Array.prototype.forEach = async function (fn) {
  // 健壮性判断
  if (typeof fn !== 'function') throw new Error('必须传递函数');
  // 判断this是数组
  if (!Array.isArray(this)) throw new Error('this必须是一个数组');

  for (let i = 0, len = this.length; i < len; i++) {
    // 自己的方法
    await fn.call(this, this[i], i, this);
  }

}