JavaScript中的异步编程

异步编程,是JavaScript编程中重要的一部分,最近学习了阮一峰老师的《深入掌握 ECMAScript 6 异步编程》系列文章,特意输出一篇学习笔记。

传统的四种异步编程方法

回调函数

JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。

假设有三个函数f1f2f3f2需要等待f1的执行结果,这时最简单的方法就是把f2写成f1的回调函数:

function f1(callback){
 setTimeout(()=>{
  console.log(1);
  callback();
 },1000);
};

function f2(){
 console.log(2);
}

function f3(){
 console.log(3);
}

f1(this.f2);
f3();

回调函数是异步编程最基本的方法,通过这种方法,函数f1不会阻塞程序的运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。

事件监听

JavaScript 事件处理是异步的。在 JavaScript 中,事件监听器会被添加到事件队列,等待主执行线程的处理。当主线程空闲时,事件队列中的事件会按顺序被执行。因此,事件回调函数不会阻塞主线程,也不会导致页面假死。

事件驱动模式,这种方法的优点是比较容易理解,并且可以同时绑定多个事件,每个事件可以指定多个回调函数,有利于实现模块化。

发布/订阅

这种异步编程是通过发布/订阅模式来实现的,这种方法的性质与“事件监听”类似。

这是一种广泛应用于异步编程的模式,是回调函数的事件化,常常用来解耦业务逻辑。事件的发布者无需关注订阅的侦听器如何实现业务逻辑,甚至不用关注有多少个侦听器存在。数据通过消息的方式可以灵活的传递。 ——《深入浅出Nodejs》

Promises对象

Promises是一个对象,它为异步编程提供了一个统一的接口,关于Promise的详细解释,我曾经做过一个很详细的用法说明。

使用Promises对象实现异步编程时,每一个异步任务返回一个Promise对象,该对象有一个then方法,开发者可以在then方法中指定回调函数。

还是以函数f1f2f3为例子,这是后的代码就应该是下面这个样子:

function f1(){
 var a = new Promise((res, rej) => {
  setTimeout(() => {
   console.log(1);
   res('1 done');
  }, 1000);
 });
 return a;
};

function f2(){
 console.log(2);
}

function f3(){
 console.log(3);
}

f1().then(() => {
 this.f2();
})
f3();

和前面几种方法相比,Promise的回调函数变成了链式写法,而且Promises对象有一整套的配套方法,可以实现很多强大的功能。

ECMAScript 6中的四种异步编程方法

Generator函数

Generator函数最大的特点就是可以暂停执行,它和普通函数的在写法上有两个区别:

  • 在函数名之前加星号以示区别
  • 异步操作需要暂停的地方,使用yield语句注明
function* gen(x){
  var y = yield x + 2;
  return y;
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }

使用星号标记以后,整个Generator函数就是一个封装的异步任务,yield语句就是异步操作要暂停的地方。

和普通函数不同的是,Generator函数执行之后返回的是一个指针对象,而不是return的值

上面的代码中,var g = gen(1);调用Generator函数会返回一个内部指针g。指针g有一个next方法,会执行异步任务的第一阶段(既指针指向内部的第一个yield语句)。next方法会返回一个对象,表示当前阶段的信息(value属性和done属性),其中value属性指的是当前yield语句后面表达式的值,done属性是一个布尔值,表示当前Generator函数是否执行完毕。

Generator函数的数据交换和错误处理

用Generator函数来处理异步编程的原因,是因为它有暂停执行和恢复执行的能力,并且Generator函数有两个特性:函数体内外数据交换机制和错误处理机制。

内外数据交换

next方法的返回值是一个对象,对象中有一个value属性,这个value属性就是Generator函数向函数体外部输出的数据。

同时,next方法也可以接受一个参数,这个参数就是向Generator函数体内输入的数据,

function* gen(x){
  var y = yield x + 2;
  return y;
}

var g = gen(1);
g.next() // { value: 3, done: false }
g.next(2) // { value: 2, done: true }
console.log(y) // 2
错误处理机制
function* gen(x){
  try {
    var y = yield x + 2;
  } catch (e){ 
    console.log(e);
  }
  return y;
}

var g = gen(1);
g.next();
g.throw('出错了')// 出错了

async函数

async函数是一种Generator函数的语法糖。和前面的Generator函数相比,在写法上async函数就是将星号替换成了async,将yield替换成了await:

async function gen(x){
  var y = await x + 2;
  return y;
}

你可能感兴趣的:(Javascript,javascript)