JavaScript异步常用方法

497 阅读3分钟

在日常代码中我们处理异步最多的估计就是回调函数,一层套一层,无间套,超过3层就会疯。

于是Promise出现了,他的出现解放了无间套,变成了then方法的堆叠,对于效率提升了非常大,但是,当过多的then方法堆在一起后,代码也不容易阅读了。

于是Generator出现了,写出来的代码也美观,但对于异步没有自动执行器,需要手动,或者再写一个自动执行器,当然有现成的co模块使用,还是没那么完美。

于是async函数出现了,问题统统解决,代码美观,还会自动执行,跟写同步代码一样,太方便了。

当然在现实中,大部分人其实用promise最多,因为超九成的开发者处理异步一般来说就是一层:主要就是接口请求,然后处理返回数据。用Promise已经完全能够胜任,现在有些人动不动就一定要别人使用async,说用Promise太落伍了,我个人认为,如果业务不复杂,用什么是个人选择,没有什么高低之分,如果业务复杂,这里就建议使用async

下面我们介绍一下这几种常用的异步处理方法。

以下所有示例直接使用setTimeout来模拟异步操作,方便测试。

回调无间

function timeProgressPromise(index, time, callback) {
  setTimeout(() => {
    callback(`通过${time}ms,第${index}步骤完成`);
  }, time)
}

timeProgressPromise(1, 1000, (res1) => {
  console.log(res1);
  timeProgressPromise(2, 1500, (res2) => {
    console.log(res2);
    timeProgressPromise(3, 2000, (res3) => {
      console.log(res3);
    });
  });
});
// 通过1000ms,第1步骤完成
// 通过1500ms,第2步骤完成
// 通过2000ms,第3步骤完成

我们可以看到这个一层套一层的写法,如果少其实还挺好看的,只是一多就不好管理了,优点是任何异步都可以用回调处理。

Promise

想用Promise那么就要把异步过程包装成Promise对象。

function timeProgressPromise(index, time) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`通过${time}ms,第${index}步骤完成`);
    }, time)
  })
}
timeProgressPromise(1, 1000)
  .then(data => {
    console.log(data);
    return timeProgressPromise(2, 1500);
  })
  .then(data => {
    console.log(data);
    return timeProgressPromise(3, 2000);
  })
  .then(data => {
    console.log(data);
  })
  .catch(err => {
    console.log(err);
  });
// 通过1000ms,第1步骤完成
// 通过1500ms,第2步骤完成
// 通过2000ms,第3步骤完成

我们可以看到通过每次then方法返回一个新的Promise就可以这样无限下去,已经非常方便了,唯一缺点就是把每次的返回数据隔离了,上一层的返回数据无法直接用在下一层,必须通过参数传下去或者中转变量。

Generator生成器

这里需要一个run方法来自动执行Generator

function timeProgressPromise(index, time) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`通过${time}ms,第${index}步骤完成`);
    }, time)
  })
}
function* myFileRead() {
  let step1 = yield timeProgressPromise(1, 1000);
  console.log(step1);
  let step2 = yield timeProgressPromise(2, 2000);
  console.log(step2);
  let step3 = yield timeProgressPromise(3, 1500);
  console.log(step3);
}
function run(fn) {
  var gen = fn();
  function next(data) {
    let res = gen.next(data);
    if (res.done) return;
    res.value.then(data => {
      next(data);
    })
  }
  next();
}
run(myFileRead);
// 通过1000ms,第1步骤完成
// 通过1500ms,第2步骤完成
// 通过2000ms,第3步骤完成

async函数

function timeProgressPromise(index, time) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`通过${time}ms,第${index}步骤完成`);
    }, time)
  })
}
async function myProgress() {
  let p1 = await timeProgressPromise(1, 1000);
  console.log(p1);
  let p2 = await timeProgressPromise(2, 2000);
  console.log(p2);
  let p3 = await timeProgressPromise(3, 1000);
  console.log(p3);
}
myProgress();
// 通过1000ms,第1步骤完成
// 通过1500ms,第2步骤完成
// 通过2000ms,第3步骤完成

小结

喜欢用哪个就用哪个,但是当你用一种方式很吃力了,建议换另一种处理方式。

生活愉快!