Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

异步编程方案汇总 #31

Open
zenglinan opened this issue Oct 20, 2019 · 0 comments
Open

异步编程方案汇总 #31

zenglinan opened this issue Oct 20, 2019 · 0 comments

Comments

@zenglinan
Copy link
Owner

异步

1. 回调函数

回调函数是最开始的异步解决方案,异步执行完后会去触发回调函数

fs.readFile('./1.txt', 'utf-8', (err, data) => {
  if(err) console.error(err)
  console.log('here is data', data)
})

这样做有几个缺点:

  1. 如果回调太多,会形成回调地狱,代码可读性会变差
  2. 难以进行错误捕获
  3. 回调套回调的方式是串行的,效率低
  4. 无法通过 return 返回数据

2. 回调函数 + 发布订阅

除了回调嵌套,我们还可以通过发布订阅模式,让多个异步任务同时进行,解决串行效率低的问题。

class eventBus {
  constructor(){
    this.bus = {}
  }
  on(eventName, callback){  // 监听
    this.bus[eventName] = callback
  }
  emit(eventName, data){  // 触发
    this.bus[eventName](data)
  }
}

const event = new eventBus()
event.on('getData', (function(){
  const collection = [] // 用来收集异步返回的数据
  return function(data) {
    collection.push(data)
    if(collection.length === 2){  // 这里要收集两个异步任务返回的数据,收集完后执行 “所有异步任务执行完成” 的回调
      console.log('here is all data!', collection)
    }
  }
}()))

只需在每次异步任务的回调里 emit 一下 getData 事件即可。每次 emit getData 事件都会触发 eventBus 通过 on 绑定的回调函数,将数据存储起来。

当存储数据达到要求时,标志着所有异步函数已经执行完毕。

fs.readFile('./1.txt', 'utf-8', (err, data) => {
  if(err) console.error(err)
  event.emit('getData', data)
})

fs.readFile('./2.txt', 'utf-8', (err, data) => {
  if(err) console.error(err)
  event.emit('getData', data)
}) 

这样写确实可以让异步并发,但是还是不够简洁,代码稍微有点复杂。

3. generator

generator 方案的出现使得函数不再是一次执行到底的了,他可以将函数分成多个执行段,将执行控制权交出。

先来看看 generator 的使用方法。

function* generator(){
  console.log(1)
  let b = yield 'a'
  console.log(2)
  console.log('b', b)
  let c = yield b
  console.log(3)
  console.log('c', c)
  return c
}

函数不会直接执行,会生成函数的迭代器对象,通过迭代器对象上的 next 方法,可以使函数段往下执行

let go = generator()
go.next() // 1

要注意的是,第一次调用 next 方法时,函数会执行到 yield 'a' 处,然后停止。yield 'a' 会返回一个形如 {value: 'a', done: false} 的对象,也就是说第一次 go.next() 的返回值是 {value: 'a', done: false}

需要注意的是,当第二次调用 next 时,b 的值并不是上一次 yield 'a' 返回的值,而是 next 函数输入的值,如下:

let go = generator()
go.next() // 1
go.next('b_value')  // 'b_value' 才是赋给 b 的值

整体流程如下:
generator

当后面没有 yield 时, returncdone 属性为 true

// ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant