js异步编程实现方法

参考文章:

基础介绍

由于 js 的单线程,如果没有异步编程方法,则主线程将直接卡死,极大的影响代码执行效率,所以 js 结合事件循环,实现了以下异步编程的方法:

  1. 回调函数
  2. 事件监听(主要是前端的元素绑定监听事件,如 onClick)
  3. 发布/订阅
  4. Promise 对象
  5. generator 函数
  6. async/await

这里所说的异步为:将任务分为两部分,先执行其中一部分,然后执行其他任务,等第二部分做好了准备,再继续执行第二部分代码。简单的说就是不连续的执行。

异步实现的主要原理都是解决: js 主线程在执行代码的时候,遇到 I/O,计时器等异步耗时任务,将任务推到事件循环中,在事件循环遍历中,检测到任务状态完成后,要如何通知主线程继续执行内部代码。image

核心需要理解的内容:

  1. 名词的定义:回调函数,发布/订阅,Promise 对象,协程,genetator 函数,参数求值策略,thunk 函数
  2. 每个方法的优缺点
  3. 什么是错误优先,为什么要将错误对象传入回调函数中
  4. Generator 函数为什么可以封装异步,是什么特性将它作为异步编程的完整解决方案
  5. js 是什么求值策略
  6. 协程执行顺序
  7. genetator 函数自动流程管理的 2 个实现方法
  8. 实现 发布/订阅(eventEmitter) , thunk 函数的自动流程管理 , promise 对象的自动流程管理

示例代码仓库:https://github.com/ddzyan/javascript-example/tree/master/%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B%E6%96%B9%E6%B3%95

回调函数

示例代码:callback.js

回调函数是 js 异步编程最基础的实现方法,开发者可以通过将第二部分任务包装在一个函数内,等第二部分做好准备了,再执行这个函数。

优点:

  1. 代码容易理解
  2. 实现方法简单

缺点

  1. 代码混乱,不容易阅读和理解
  2. 代码耦合度高,多层回调容易造成回调地狱

这里需要提到的是,nodejs 约定回调函数的第一个参数为错误对象,第二参数为传入的值,这就是错误优先原则。原因为代码在执行第二部分的回调函数时候,原本的上下文环境已经结束,无法捕捉抛出的异常,所以将异常传入回调函数,进行处理。

发布/订阅

示例代码:eventEmitter.js
采用事件驱动的模式,将第二部分代码包装在消息订阅的回调函数内,当第二部分准备好了,则通过消息发送通知执行第二部分代码。nodejs 的核心就是采用了 v8 引擎实现了事件驱动,异步非堵塞 I/O。

优点:

  1. 代码耦合度低

缺点

  1. 代码分散,流程不容易理解

Promise 对象

示例代码:promise.js

Promise 为 es6 中的新语法,它的原理是将每个异步任务封装返回一个 Promise 对象,对象内管理着异步任务执行状态,当任务完成则通过 resolve 函数进行回调,失败则通过 reject 进行回调。

Promise 的写法是对回调函数进行改进,将原来回调函数的嵌套调用,修改为链式调用,使代码流程更容易理解。

优点:

  1. 链接调用,代码流程容易理解
  2. 代码耦合度低

缺点:

  1. 每个结果都需要进行 retudn,然后在 then 方法进行获得,造成代码量冗余
  2. 语法比较复杂

generator 函数

协程

协程为多个线程,协助完成异步任务,执行流程如下:

  1. 第一步,协程 A 开始执行
  2. 第二步,协程 A 执行到一半,进入暂停状态,执行权转接到协程 B
  3. 第三步,协程 B 过一段时间后,将执行权转接到 协程 A
  4. 第四部,协程 A 继续执行
    这里的协程 A 就是异步任务

协程的 Generator 函数实现

示例代码:generator.js

Generator 函数就是协程在 es6 中的实现,最大的特点就是可以交出函数的执行权(暂停执行代码)。函数执行返回一个遍历器对象,对象内具有 next 方法,每次执行 next ,才会执行内部的 yiel 后的异步方法,返回一个对象。对象具有 2 个属性,value 代表异步返回结果,done 代表遍历是否完成。

Generator 函数可以暂停和恢复执行,这是它可以封装异步任务的根本原因。
Generator 函数的 2 个特性,使他可以作为异步编程的完整解决方案:

  1. 错误捕捉
  2. 数据交换

优点:

  1. 异步流程便是简洁

缺点:

  1. 无法自动进行流程管理

Generator 函数是一个异步操作容器,它的自动执行需要一种机制,当异步操作有了结果,需要交还代码执行权力。可以实现的两种方法如下:

  1. 回调函数。将异步操作封装成一个 thunk 函数,在 thunk 函数 内交还代码执行权。示例代码:thunk-callback-generator.js
  2. promise 对象。将异步操作封装成一个 promise 对象,用 then 方法交还代码执行权,示例代码:promise-generator.js

thunk 函数

编译器的 传名调用实现,将参数放到一个临时函数中,再将这个临时函数传入到函数体中,这个临时函数就被称为 thunk 函数

1
2
3
4
5
6
7
8
9
10
11
function a() {
console.log(...args);
}

function b(a) {
return function() {
a(...args);
};
}

b(a)(1, 2, 3);
参数求值策略
1
2
3
4
5
function f(a, b) {
return b;
}

f(3 * x * x - 2 * x - 1, x);
  • 传值调用:参数在调用执行,就已经进行了计算,传入的为最终计算结果
  • 传名调用:只有在使用到参数的时候,才会对参数进行计算。

js 是传值调用

0%