Skip to content

异步编程

JavaScript单线程特性

JavaScript为什采用单线程?

JavaScript是以单线程模式执行代码(同一时间只能做一件事),采用单线程方式工作的原因与其最开始设计初衷有关。JavaScript最早是运行在浏览器的脚本语言,用于处理页面的动态交互,其核心就是dom操作,采用单线程模式是为了避免复杂的多线程同步问题(当同时操作一个dom元素时,无法明确以哪个工作线程为准)。

单线程

指的是JavaScript负责执行代码的线程只有一个,这也就意味着所有任务必须排对执行,采用单线程的工作模式线程利用率极高,可以节省内存,节约上下文切换时间,没有多线程锁的问题,但如果碰到耗时长的任务,就是导致页面阻塞(页面假死)的情况。为了解决这一问题,JavaScript将任务执行模式分成两种———同步模式异步模式

同步模式与异步模式

同步模式:排队依次执行。

异步模式:开启执行之后,立即执行下一个任务,后续逻辑在回调函数里面定义。

JavaScript如何实现异步模式的呢?

在代码运行中会同时存在大量的同步任务异步任务,那么JavaScript是如处理同步任务异步任务任务的执行顺序呢?这里就引出了事件轮询(EventLoop),它规定了任务在浏览器中的执行顺序,浏览器在运行应用时,通过宏任务/微任务的类型,将不同的任务放到不同的消息队列等待执行。

事件轮询/消息队列

图片

宏任务/微任务

宏任务:可以理解为每次执行栈执行的代码就是一个宏任务。宏任务有————script代码块、setTimeout、setInterval、I/O、UI交互事件、MessageChannel等。

浏览器为了让JavaScript内部宏任务与DOM操作能够有序的执行,会在一个宏任务执行结束后,下一个宏任务执行开始前,对页面进行重新渲染

微任务:每个宏任务执行结束后立即执行的任务,发生在宏任务后,渲染之前,执行微任务。微任务有————Promise.then、MutaionObserver、process.nextTick(Node.js环境下)

微任务的响应速度相比宏任务会更快,因为无需等待UI渲染。

回调函数

回调函数: 由调用者调用,执行者执行。所有异步编程的根本就是回调函数。

Promise

在JavaScript ES6之前,异步编程主要依赖于回调函数。回调函数虽然简单,但存在“回调地狱”(callback hell)的问题,使得代码难以维护。Promise的出现,为异步编程提供了一种更清晰、更结构化的方法。

Promise是一个代表异步操作的对象,它有三种状态:

Pending(进行中):初始状态,既不是成功,也不是失败状态。

Fulfilled(已成功):操作成功完成。

Rejected(已失败):操作失败。

Promise的状态只能从 Pending 变为 Fulfilled 或 Rejected,并且状态一旦改变,就不可逆

注意

Promise.then每次返回的是一个状态明确,新创建的promise对象,保证其可以链式调用。

基本应用

js
function ajax(url){
    return new Promise((resolve,reject)=>{
        let xhr = new XMLHttpRequest();
        xhr.open('GET',url);
        xhr.responseType = 'json';
        xhr.onload = function(){
            if(this.status === 200){
                return resolve(this.response)
            }else{
                return reject(new Error(this.statusText))
            }
        }
        xhr.onerror = () => reject(new Error('Network error'));
        xhr.send();
    })
}
ajax('./api/user')
function ajax(url){
    return new Promise((resolve,reject)=>{
        let xhr = new XMLHttpRequest();
        xhr.open('GET',url);
        xhr.responseType = 'json';
        xhr.onload = function(){
            if(this.status === 200){
                return resolve(this.response)
            }else{
                return reject(new Error(this.statusText))
            }
        }
        xhr.onerror = () => reject(new Error('Network error'));
        xhr.send();
    })
}
ajax('./api/user')

Generator

js
function* foo() {
  yield 'result1'
  yield 'result2'
  yield 'result3'
}
  
const gen = foo()
console.log(gen.next().value)
console.log(gen.next().value)
console.log(gen.next().value)
function* foo() {
  yield 'result1'
  yield 'result2'
  yield 'result3'
}
  
const gen = foo()
console.log(gen.next().value)
console.log(gen.next().value)
console.log(gen.next().value)

Generator需要封装一个执行器

async/await

async/await 是对Generator的语法糖。

js
async function fetchData(url) {
    try {
        const response = await fetch(url);
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Error fetching data:', error);
    }
}
async function fetchData(url) {
    try {
        const response = await fetch(url);
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Error fetching data:', error);
    }
}