js异步操作

整理常用的几种js异步操作方法

Posted by Aaronwn on January 3, 2018

同步、异步概念

“同步模式”就是后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的; “异步模式”则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

【异步模式】编程的常用方法

回调函数

传说中的“callback hell”就是来自回调函数。而回调函数也是最基础最常用的处理js异步操作的办法。我们来看一个简单的例子: 首先定义三个函数:

function fn1 () {
  console.log('Function 1')
}

function fn2 () {
  setTimeout(() => {
    console.log('Function 2')
  }, 500)
}

function fn3 () {
  console.log('Function 3')
}

其中fn2可以视作一个延迟了500毫秒执行的异步函数。现在我希望可以依次执行fn1,fn2,fn3。为了保证fn3在最后执行,我们可以把它作为fn2的回调函数:

function fn2 (f) {
  setTimeout(() => {
    console.log('Function 2')
    f()
  }, 500)
}

fn2(fn3)

可以看到,fn2和fn3完全耦合在一起,如果有多个类似的函数,很有可能会出现fn1(fn2(fn3(fn4(…))))这样的情况。回调地狱的坏处我就不赘述了。

Promise

function fn1 () {
  console.log('Function 1')
}

function fn2 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Function 2')
      resolve()
    }, 500)
  })
}

function fn3 () {
  console.log('Function 3')
}

同样的,我们定义了三个函数,其中fn2是一个返回Promise的异步函数,现在我们希望按顺序执行它们,只需要按以下方式即可:

fn1()
fn2().then(() => { fn3() })

// output =>
// Function 1
// Function 2
// Function 3

使用Promise与回调有两个最大的不同,第一个是fn2与fn3得以解耦;第二是把函数嵌套改为了链式调用,无论从语义还是写法上都对开发者更加友好

优雅的async/await

那么为什么说async/await方法是最优雅的呢?且看代码:

function fn1 () {
  console.log('Function 1')
}

function fn2 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Function 2')
      resolve()
    }, 500)
  })
}

function fn3 () {
  console.log('Function 3')
}

async function asyncFunArr () {
  fn1()
  await fn2()
  fn3()
}

asyncFunArr()

// output =>
// Function 1
// Function 2
// Function 3

有没有发现,在定义异步函数fn2的时候,其内容和前文使用Promise的时候一模一样?再看执行函数asyncFunArr(),其执行的方式和使用generator的时候也非常类似。

异步的操作都返回Promise,需要顺序执行时只需要await相应的函数即可,这种方式在语义化方面非常友好,对于代码的维护也很简单——只需要返回Promise并await它就好,无需像generator那般需要自己去维护内部yield的执行。

尾声

作为一个归纳和总结,本文内容的诸多知识点也是来自于他人的经验,不过加入了一些自己的理解和体会。不求科普于他人,但求作为个人的积累。