Skip to content
Branch: master
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
README.md
async-await.js
callback.js
eventEmitter.js
generator.js
package.json
promise-generator.js
promise.js
thunk-callback-generator.js

README.md

参考文章:

基础介绍

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

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

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

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

核心需要理解的内容:

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

示例代码仓库:

回调函数

示例代码: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 函数

参数求值策略
function f(a, b) {
  return b;
}

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

由于 js 是传值调用 ,编译器需要通过封装实现传名调用。

将参数放到一个临时函数中,再将这个临时函数传入到函数体中,这个临时函数就被称为 thunk 函数

function a() {
  console.log(...args);
}

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

b(a)(1, 2, 3);

js 中的 thunk 函数:将一个多参数函数,替换为只接收一个回调函数作为参数的单参数函数

function a(name, callback) {
  callback(name);
}

// 正常版本
a(name, callback);

// 单回调函数参数版本
function a() {}

function thunk(name) {
  return function(callback) {
    a(name, callback);
  };
}

const aThunk = thunk(name);
aThunk(callback);
You can’t perform that action at this time.