从callback到async

本文不是概念性文章,只是经过一个例子来看看操做异步的一些手段.html

回调函数

先看一个例子:node

const fs = require('fs')

fs.readFile('./file.txt', 'utf-8', (err, res) => {
  console.log(res)
})
复制代码

经过 node 提供的 fs.readFile 方法打印出file.txt的内容.json

若是readFile能够经过 promise 方式调用就行了, 像下面这样:api

readFile('./file.txt', 'utf-8').then(res => {
  console.log(res)
})
复制代码

promise

如今咱们将 callback 形式的方法调用 转成 promise 形式的调用,这个过程也成为 promisifypromise

const promisify = fn => (...args) =>
  new Promise((reslove, reject) =>
    func(...args, (err, result) => (err ? reject(err) : resolve(result)))
  )
复制代码

固然,在 node 8 及其以上版本,能够使用util.promisify异步

如今就能够这样作啦async

const fs = require('fs')
const readFile = promisify(fs.readFile)
readFile('./file.txt', 'utf-8').then(res => {
  console.log(res)
})
复制代码

统一的 API,线性的调用方式,更为灵活的异步控制,exciting!函数

async

ES7(ECMAScript 2016)推出了 Async 函数(async/await),实现了以顺序/同步代码的编写方式来控制异步流程.再来一遍post

const fs = require('fs')
const util = require('util')
const readFile = util.promisify(fs.readFile)

;(async () => {
  const res = await readFile('./package.json', 'utf-8')
  console.log(res)
})()
复制代码

使用 generator 与 promise 模拟 co

相较于 promise,async/await 更易理解. 若是你听过大名鼎鼎的 co,则会发现这两者使用方式是多么类似啊!优化

const co = require('co')
const fs = require('fs')
const util = require('util')
const readFile = util.promisify(fs.readFile)
co(function*() {
  const res = yield readFile('./package.json', 'utf-8')
  console.log(res)
})
复制代码

不过这并非什么黑魔法,继续向下看.

generator

首先咱们须要一点 generator 的知识,你能够看Generator-MDN

若是你不想看,看下 demo 也能够

demo 1

function* generatorFn() {
  yield 1
  yield 2
  yield 3
}

const it = generatorFn()

console.log(it.next()) // { value: 1, done: false }
console.log(it.next()) // { value: 2, done: false }
console.log(it.next()) // { value: 3, done: false }
console.log(it.next()) // { value: undefined, done: true }
复制代码

demo 2

function* generatorFn() {
  const a = yield 1
  console.log(a) // hhh
  yield 2
  yield 3
}

const it = generatorFn()
console.log(it.next()) // { value: 1, done: false }
console.log(it.next('hhh')) // { value: 2, done: false }
console.log(it.next()) // { value: 3, done: false }
console.log(it.next()) // { value: undefined, done: true }
复制代码

弄清楚代码执行顺序便可。

小练习

const fs = require('fs')
const util = require('util')
const readFile = util.promisify(fs.readFile)

function go() {}

go(function*() {
  const file1 = yield readFile('./file1.txt', 'utf-8')
  console.log(file1)
  const file2 = yield readFile('./file2.txt', 'utf-8')
  console.log(file2)
})
复制代码

解答

思路很简单,咱们只要在 go 中接住 yield 抛出来的 promise,注册好"解决函数"(resolver),在 resolver 内部将执行结果塞进去便可.

先写一个最挫的,只能玩 2 层,和咱们的代码绑定的死死的.

function go(gen) {
  const it = gen()
  const p1 = it.next().value
  p1.then(res => {
    const p2 = it.next(res).value
    p2.then(res => {
      it.next(res)
    })
  })
}
复制代码

很明显,能够优化一波

function go(gen) {
  const it = gen()

  const run = p => {
    p.then(res => {
      const { value, done } = it.next(res)
      if (done) {
        // 若是结束了就返回
        return value
      }
      run(value)
    })
  }
  const { value, done } = it.next()
  if (done) {
    return value
  }
  run(p1)
}
复制代码

本文到此为止,代码很粗糙,下一篇文章拿 co 源码来读读。


推荐阅读:

相关文章
相关标签/搜索