异步编程


什么是异步编程

  • 同步编程:一件事情做完之后才能去做另外一件事情(单线程)

  • 异步编程:上一件事情没有处理完的情况下,就可以去做下一件事情(多线程)

JS 代码在浏览器中执行,而浏览器是多进程的,当我们打开一个界面的时候就相当于开启了一个进程,在这一个进程里会存在多个线程(JS引擎、GUI渲染、HTTP网络请求、DOM事件监听器、定时器监听线程…),其中JS引擎就来执行JS代码,很明显它是一个单线程,因为它是单线程所以默认情况下它只能同步完成代码执行,但是JS可以实现异步,这是因为它内部存在事件循环和事件队列机制。

同步和异步

单线程

Javascript语言的执行环境是”单线程”(single thread)。

所谓”单线程”,就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推

同步

“同步模式”就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的

生活中的同步是指同一时间只能处理一件事情

异步

“异步模式”则是每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的

事件循环

  1. 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

  2. 主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个执行任务。定时器setTimeout是时间为0 后才添加到任务队列里

  3. 一旦”执行栈”中的所有同步任务执行完毕,事件循环系统就会读取”任务队列”,看看里面有哪些对应的异步任务,按照于是结束等待状态,进入执行栈,开始执行。

  4. 主线程不断重复上面的第三步

根据规范:事件循环是通过 「任务队列」 的机制来进行协调的。一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),「源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列」 setTimeout/Promise 等API便是任务源,而进入任务队列的是他们指定的具体执行任务

EventLoop

异步任务分类

宏任务macrotask

发起者:宿主(Node、浏览器)
事件:script(整体代码)、setTimeout、setInterval、setImmediate(Node.js 环境)、UI事件、I/O(Node.js)
运行:后运行

微任务microtask

发起者: JS引擎
事件:Promise、MutaionObserver、process.nextTick(Node.js)
运行:先运行

微任务优先级高于宏任务,先执行完所有微任务,宏任务在之后执行

关系图

示例动画

回调函数

回调函数是一个函数,将会在另一个函数完成执行后立即执行。回调函数是一个作为参数传给另一个 JavaScript 函数的函数。这个回调函数会在传给的函数内部执行。

  • 常见的回调函数:ajax的success/定时器的函数/jquery动画里的函数
  • 作用:进行后续操作,起到一个通知的作用
  • 应用:
    • 定时器(异步回调)
    • ajax(异步回调)
    • 事件处理程序(异步回调)
    • sort

分类

  • 根据是否是同步操作还是异步的操作,将回调函数分为同步回调和异步回调两种

同步回调

  • 回调函数的代码必定是按照书写顺序来执行的
  • 立即执行,完全执行完才结束,不会放入回调队列中
    • 例子:数组遍历相关的回调函数
function task1(callback){
    console.log('任务1执行中');
    callback();
}
function task2(){
    console.log('任务2执行中');
}
task1(task2);

异步回调

  • 回调函数的执行跟书写顺序不一致。
  • 不会立即执行,会放入回调队列中将来执行
    • 例子:定时器回调、ajax回调、promise的成功失败回调
setInterval(() => {
    console.log('1000后执行');
}, 1000);
console.log('定时器之后的代码,会在回调函数之前执行');

回调地狱

背景

  • 实际应用中,很多时候,需要多个异步操作能够按照顺序执行。比如先获取电影数据的个数,获取数据的个数后再获得具体的电影数据,还需要获取电影类型的数据。这样导致了回调函数的不断嵌套

概述

  • 回调地狱是指因为多个异步操作需要按一定顺序执行,导致每个异步操作的回调函数代码形成了多层嵌套。导致了代码阅读不易,维护不方便
// 获取电影个数:第一个异步操作
$.ajax({
    url:"地址",
    success:function(data){
        //拿到个数,根据个数在页面上生成对应数量的标签
        //拿到具体的数据:第二个异步操作
        $.ajax({
            url:"地址",
            success:function(data){
                //拿到电影数据
                //拿到分类数据:第三个异步操作
                $.ajax({
                    url:'',
                    success:function(){
                    }
                })
            }
        })
    }
});

解决办法:promise, async&await 处理有异步代码顺序执行的问题


文章作者: 时光路人
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 时光路人 !
评论
  目录