JavaScript异步编程的5种方式

Javascript语言的执行环境是”单线程”.这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。

1.回调函数

这是异步编程最基本的方法。

1
2
3
4
5
6
7
8
//如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数。
function f1(callback){
    setTimeout(function () {
      // f1的任务代码
      callback();
    }, 1000);
  }
f1(f2);

回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。

2.事件监听

采用事件驱动模式进行异步编程。任务的执行不取决于代码的顺序而取决于某个事件的发生。

例如:element.addEventListener(event, function, useCapture)。通过addEventListener监听事件的发生是最原始的js模式。

3.发布/订阅

观察订阅者模式

4.promise 对象

Promise 是通过es6正式发布,是es6最重要的特性之一。是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

(1)对象的状态不受外界影响。promise是一个异步操作,有三种状态,pending进行中,resolve已成功,reject已失败。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。

1
2
3
4
5
6
7
8
9
10
const promise = new Promise(function(resolve, reject) {
// ... some code

if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});

promise构造函数接收一个函数作为参数,而该函数接受两个参数为reject,resolve。resolve代表从未完成到成功的状态是,reject代表从未完成到失败的状态。
缺点无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

5.async await

async字面上的意思就能看出是异步。那么async实际上到底做了什么呢?我们可以用代码看看当我们打印一下async函数看看到底返回了什么。代码如下:

1
2
3
4
5
async function asyncFunc(){
return "async function";
}
let test = asyncFunc()
console.log(test)

运行之后你会发现返回的其实就是一个promise对象。
图片

那么await又在这里起了什么作用呢。从字面意思也能看出,await就是在等待这个promise完成并且返回其结果出来。而await的原理又是什么呢?我们执行以下代码看看到底做了什么返回了什么。

1
console.log(await test)  //"async function"

可以看出其实就是返回了promise执行的结果。
其实await底层原理是应用了es6的generator生成器。具体使用可以看看阮一峰的教程。

看了上面的这个异步处理方法大家是不是很迷惑那为什么不直接用promise就好了吗。

区别:

1.写法更便捷,无需链式调用。

2.Promise 是应用层的解决方案,它有一个规范,不同的语言也可以实现,它只能异步的处理错误,在js 里它本质上是一个对象。async-await 是语言层的解决方案,它可以说是 Promise的补充,可以让用户像编写同步代码一样编写异步代码,通过try-catch 可以同步地处理错误。

3.async-await 必须配合一起使用,也有前后关系,可读性更好。