JavaScript异步

By :  司嘉年(moke)

JavaScript是单线程?

  • JS引擎中负责解释和执行JS代码的线程只有一个(主)
  • AJAX、处理DOM、定时器、读写......(工作线程)
  • 对于耗时或者时间不确定的操作,使用异步
  • html5 web worker 多线程

异步执行机制

  1. 所有同步任务都在主线程执行,形成执行栈
  2. “任务队列”,异步任务的结果存放
  3. 同步任务执行完毕,读取“任务队列”
  4. 主线程不断重复第三步
  5. 主线程阻塞,任务队列依旧能够被推入任务

(by:阮一峰的网络日志,再谈Event Loop)

Event Loop

主线程从“任务队列”中读取事件,不断循环

(by:阮一峰的网络日志,再谈Event Loop)

Macrotask 和 Microtask

macro-tasks :script(整体代码),setTimeout,setInterval,setImmediate,I/O,UI

 

micro-tasks :process.nextTick,Promises,Object.observe,MutationObserver(html5新特性)

在一个事件循环里,

这两个队列会分两步执行,

第一步会固定地执行一个(且仅一个)Macrotask任务,

第二步会执行整个Microtask队列中的所有任务。

 

用代码来感受

作用

Macrotasks : 代表一些离散的独立的工作,task结束后,浏览器继续其他工作如页面重渲染和垃圾回收等

Microtasks : 更新完成应用程序状态的较小任务,在UI重渲染之前执行某些任务,避免不必要的UI渲染

Vue 中 MutationObserver

var counter = 1
var observer = new MutationObserver(nextTickHandler)
var textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
    characterData: true
})
timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
}

JS异步发展

  • Callback
  • Promise
  • Generator
  • Async function
  • rxjs ??
  • ......

Callback

  • 给匿名函数命名
  • 模块化
  • ...
getData(function(a){  
    getMoreData(a, function(b){
        getMoreData(b, function(c){ 
            getMoreData(c, function(d){ 
                getMoreData(d, function(e){ 
                    ...
                });
            });
        });
    });
});

Promise

// 没有 promise
a(getResultFromA, (aResult, err) => {
  if (!err) {
    b(getResultFromB, (bResult, err) => {
      if (!err) {
        c(getResultFromC, (cResult, err) => {
          if (!err) {
            // do something
          } else {
            throw err
          }
        })
      } else {
        throw err 
      }
    })
  } else {
    throw err
  }
})

// 用了 promise 后
new promise((resolve, reject) => {
  a(getResultFromA, (aResult, err) => {
    if (!err) resolve(aResult) else reject(err)
  })
})
.then(data => {
  return new Promise((resolve, reject) => {
    b(getResultFromB, (bResult, err) => {
      if (!err) resolve(bResult) else reject(err)
    })
  }
})
.then(data => {
  return new Promise((resolve, reject) => {
    c(getResultFromC, (cResult, err) => {
      if (!err) resolve(cResult) else reject(err)
    })
  }
})
.then(data => {
  // do something
})
.catch(err => {
  throw err
})
  • 解决了异步回调函数层层嵌套
  • 错误处理以及链式回调

Generator

// 定义 
var fetch = require('node-fetch');

function* gen(){
  var url = 'https://api.github.com/users/github';
  var result = yield fetch(url);
  console.log(result.bio);
}

// 使用
var g = gen();
var result = g.next();

result.value.then(function(data){
  return data.json();
}).then(function(data){
  g.next(data);
});
  • "协程"(coroutine),意思是多个线程互相协作,完成异步任务
  • 流程管理却不方便

Async

  1. 内置执行器
  2. 更好的语义
  3. 更广的适用性
  4. 返回值是Promise

Rxjs(流Stream)

Reactive Extensions for JavaScript

观察者+迭代器模式

Observables与Observer

  • 订阅:Observer通过Observable提供的subscribe()方法订阅Observable
  • 发布:Observable通过回调next方法向Observer发布事件

Thanks

参考:ECMAScript 6 入门(阮一峰)

最后谈一次 JavaScript 异步编程(brambles)

Tasks, microtasks,

queues and schedules What the heck is the event loop anyway?

Concurrency model and Event Loop Macrotasks and Microtasks

......

 

JavaScript 异步

By moke

JavaScript 异步

  • 740