写在前面, 这个话题其实还挺大的, 我自己恐怕力有不逮, 所以只能算是笔记总结, 写的肯定会有点简略. 有错误实在太正常了. 希望能多多指教.

这篇文章仅仅是解释一下现有的异步编程方案不涉及具体原理, 但是我的想法是试试看能不能每个方案都自己实现一遍, 所以可能是系列文章也可能就此太监了.

回调函数

image

想必大家都看过上面的图片, 虽然不是js代码, 甚至不是回调, 但是各位同学估计对callback hell感同身受. 但是事实上回调函数跟异步编程并没有必然的联系. 回调只是一种设计模式. 同步代码同样可以使用回调只是大部分时候会让人觉得多此一举而已.

大家都知道回调并不是那么优雅, 很多时候代码可能写成这样:

asyncJob1(function () {
  asyncJob2(function () {
    asyncJob3(function () {
      // your code
    })
  })
})

虽然问题算是解决了吧, 但是代码看着真是令人头痛. 玩意逻辑稍微复杂一点说不定真会出现下面的情况:
image

大概美军也是回调写多了吧2333.

事件驱动

大部分gui程序都是采用事件驱动, web当然也不例外, 最典型的就是dom事件:

div.on('click', function () {
  // balabala
})

这个几乎都是跟回调和事件循环相关的, 后面会详细讲到的.

Promise

根据promise/A+的规范:

一个Promise必须处在其中之一的状态:pending, fulfilled 或 rejected.
如果是pending状态,则promise可以转换到fulfilled或rejected状态。
如果是fulfilled状态,则promise不能转换成任何其它状态。
如果是rejected状态,则promise不能转换成任何其它状态。

Promise都会有个then方法, 制定了fulfilled和rejected两种请况的回调, 同时then会返回一个Promise对象, 这就允许我们链式调用了:

ajax('xxx').then(res => res).then(res => res)

虽然Promise的方案比回调已经好太多了. 但是不难发现多个then其实未必比回调好看太多, 只能说把回调铺平了. 可能Promise并不是最优雅的解决方案.

generator

generator在各个语言里都有出现, python,c#等, 这里不说概念, 只说在js里该怎么使用就行, 基本理解就是generator是拥有多个返回值的函数, 每次调用next就会调用一次返回. 这就是所谓 控制权移交, 当然这样不足以体现其优越性, generator最重要的特性就是分步数据传递: next的参数可以作为上次yield的返回值, 其实直觉上不是很能理解, 不过我们这里只要知道有这么个东西就成, 下面是代码示例:

var f = function* (x) {
    var y = yield x + 1
    yield y + 1
    return y
}


var t = f(1)

var s = t.next()
console.log(s) // 2
var p = t.next(4) // 这里的4作为上次yield的返回值
console.log(p) // 5

有这么一个特性, 那么下面的操作性就很强了, 比如tj的著名的co:

co(function* () {
  var result = yield Promise.resolve(true);
  return result;
}).then(function (value) {
  console.log(value);
}, function (err) {
  console.error(err.stack);
});

可以说写法已经很同步了. 当然这个也不是最终的解决方案, 不然为什么koa2要用async/await呢hhh

async/await

同样对于async/await 我们先不纠结其原理, 我们只需要知道它是generator和promise二者结合起来的语法糖(不过也真够甜的)

每个async必然返回一个Promise对象, 所以async会像瘟疫一个把你的每个函数都变成async函数, 所谓可以async的终将async, 下面是例子:

// Promise的解决方案
function foo () {
  return ajax('xxx').then(res => res).then(res => res)
}


async function bar () {
  try {
    const response = await ajax('xxx')
    console.log(response)
  } catch (e) {
    throw e
  }
}

是不是优雅了太多, 除了一大片async/await, 不过也算小小的代价.

利用午休时间码了出来, 不知道有多少错误呢hhh

image


xiadd
2.6k 声望88 粉丝