你不知道的JavaScript---异步:现在与未来

2023-11-16

目录

异步

分块的程序

事件循环

并行线程

并发

非交互

交互

协作

任务

语句顺序


异步

js如何表达和控制持续一段时间的程序行为(分散在一段时间内运行的程序行为)

​ 持续一段时间,不是指类似于 for循环开始到结束的过程。而是指 程序的一部分现在运行,而另一部分则在未来运行。现在与将来之间有一段间隙,这段间隙在实际程序中,可以是等待用户输入、从数据库或文件系统中请求数据、通过网络发送数据并等待响应,或者是在以固定时间间隔执行重复任务(比如动画)。

  • JavaScript中的异步,是指程序一部分现在运行,而一部分则在将来运行——现在和将来有时间间隔,在这段间隔中,程序没有活跃执行。
  • 程序现在运行的部分和将来运行的部分之间的关系就是异步编程的核心

分块的程序

​ 无论JS程序是在多个JS文件或一个JS文件,JS中都是都是由一个个块组成。同一时间只有一个块可以在 现在执行。最常见的块就是 函数

var data = ajax("http://some.url.1");
console.log(data);  // data通常并不会出现Ajax的结果

因为标准Ajax请求不是同步完成的,当打印data时,ajax函数可能还没有返回任何值给变量data。

​ 如何能够确定得到ajax函数的返回值?1、将ajax(...)能够阻塞到响应返回,即发出ajax请求后什么事也不做,直到得到返回值。这与我们希望将一部分值在 未来运行是相违背的。

  • 程序中将来执行的部分并不一定在现在运行的部分执行完之后就立刻执行。比如:Ajax发送请求,需要等待数据返回之后,才会执行“将来”的部分。
  • 从现在到将来的“等待”,最简单的方法是使用一个通常称为回调函数的函数。(不是唯一的方案)

function now() {
	return 21;
}
function later() { // 将来执行的部分
	answer = answer * 2;
	console.log("Meaning of life:", answer);
}
var answer = now();
setTimeout(later, 1000); // Meaning of life: 42

 任何时候,只要把一段代码包装称一个函数,并指定它在响应某个时间(定时器、鼠标点击、Ajax响应等)时执行,你就在代码中创建了一个将来执行的块,也就在这个程序中引入了异步机制

② 异步控制台

ar a = {index: 1}; 
console.log(a.index); // 1
console.log(a); // 在浏览器控制台中查看为 {index: 2}
a.index++;

  • 原因:浏览器会认为需要把控制台I/O延迟到后台,这种情况下,等到浏览器控制台输出对象内容时,a.index++可能已经执行了,因此会显示 {index: 2}

某些浏览器的console.log()并不会把传入的内容立即输出,可能出现异步情况。这种情况比较少见,最好的选择是使用断点调试。

事件循环

​ JS引擎并不是独立运行的,它运行在宿主环境中,大多数为Web浏览器,也有如Node.js等服务器端环境。​ 这些环境提供一种机制来处理程序中多个块的执行,且执行每块时调用JS引擎,这种机制被称为 事件循环。 可以按照下面的伪代码来理解事件循环:

var eventLoop = [];
var event;

//“永远”执行
while (true) {
    // 一次tick
    if (eventLoop.length > 0) {
        event = eventLoop.shift();
        try {
            event();
        }
        catch (err) {
            reportError(err);
        }
    }
}

setTimeout(...) 定时器精度不高原因

setTimeout(…) 并没有把你的回调函数挂在事件循环队列中。它所做的是设定一个定时器。当定时器到时后,环境会把你的回调函数放在事件循环中,这样,在未来某个时刻的 tick 会摘下并执行这个回调。

如果这时候事件循环中已经有 20 个项目了会怎样呢?你的回调就会等待。它得排在其他项目后面——通常没有抢占式的方式支持直接将其排到队首。这也解释了为什么setTimeout(…) 定时器的精度可能不高。大体说来,只能确保你的回调函数不会在指定的时间间隔之前运行,但可能会在那个时刻运行,也可能在那之后运行,要根据事件队列的状态而定。

也就是说,定时器只能保证回调函数在设定的时间之后执行,而不是到达设定的时间就会执行,是在设定的时间到了之后入队列,等到调用栈为空时候拿出执行

并行线程

tips:进程和线程??❤❤❤❤❤❤❤

进程和线程是不同的操作系统管理资源的方式,进程有自己独立的内存空间,线程没有独立地址空间,但是有自己堆栈和局部变量,线程是进程的一个实体,是CPU任务调度和分配的基本单位,线程不能独立执行,必须依存在进程之中,同一进程的多个线程共享同一内存空间,进程退出时改进程的所有线程会被清空

”异步“与”并行“常常被混为一谈,但实际上它们的意义完全不同。

  • 异步:关于现在将来的时间间隙。

    事件循环把自身的工作分成一个个任务并顺序执行,不允许对共享内存并行访问和修改。通过分立线程中彼此合作的事件循环,

  • 并行:关于能够同时发生的事情。并行和顺序执行可以共存。

    并行最常见的工具有进程线程。进程与线程独立运行,并可能同时运行,多个线程能够共享单个进程的内存。
    ​​​​

JS不支持跨线程共享数据( 单线程 ),并且具有 完整运行特性,即如果有两个函数执行,两个函数不会交替运行,一定是先完整执行第一个函数,然后才是第二个函数。

虽然JS不需要考虑 线程层次的不确定性,但是依然存在 竞态条件,考虑下面的代码:

我们无法预料哪个ajax会先请求到数据,但是不论是谁先请求到数据,在它执行回调函数的时候内部不会被打断,所以它只可能有两种结果,这取决与谁先请求到数据。

“竞态”就是在多线程的编程中,你在同一段代码里输入了相同的条件,但是会输出不确定的结果的情况

var a = 0;
function foo(){
    a += 1;
}
function bar(){
    a += 2;
}
// 两个方法会并行运行(实际上并不是同时发生的,先1后2,但异步时间的间隙无法确定先后),但最终的结果无法预测
ajax('http://xxxx.url.1', foo);
ajax('http://xxxx.url.2', bar);

并发

​ 设想一个状态更新列表,随着用户向下滚动列表而住就按加载更多内容。这里需要两个”进程“,一个监听页面滚动触发onscroll,并发起Ajax请求,另一个接受Ajax请求,并把内容展示到页面。当用户滚动页面时,可能在等待第一个响应的同时,就会有第二、第三个请求发出。

两个或多个”进程“同时执行就出现了并发,不管组成它们的单个运算是否 并行执行(在独立的处理器或处理器核心上同时运行)。可以把并发看作”进程级的并行,与运算级的并行(不同处理器上的线程)相对。

  • 如果并发的”进程“需要通过作用域DOM间接监护,就需要对交互进行协调,避免竞态的出现。

非交互

下面的程序,无论按哪种顺序执行都无所谓,foo可能先执行,bar可能先执行,但因为它们是独立运行的,所以不会相互影响。

var res = {}; 
function foo(results) { 
 res.foo = results; 
} 
function bar(results) { 
 res.bar = results; 
} 
ajax( "http://some.url.1", foo ); 
ajax( "http://some.url.2", bar );

交互

var res = []; 
function response(data) { 
 res.push( data ); 
} 
ajax( "http://some.url.1", response ); 
ajax( "http://some.url.2", response );

上面的程序,由于执行顺序的不确定性,会导致res中数据的存放顺序也是不确定的。假定我们期望res[0]存放"http://some.url.1"的结果,res[1]存放"http://some.url.2"的结果,在这种情况下,我们就需要协调交互顺序。下面是一种做法:

var res = []; 
function response(data) { 
 if (data.url == "http://some.url.1") { 
    res[0] = data; 
 } 
 else if (data.url == "http://some.url.2") { 
    res[1] = data; 
 } 
} 
ajax( "http://some.url.1", response ); 
ajax( "http://some.url.2", response );

协作

考虑这里有一个执行时间特别长的程序,它的执行会阻塞其他程序的进行,因为JS是单线程的,同时UI不能进行刷新,因为JS引擎线程与渲染线程互斥,此时我们就需要将它分割成多个步骤或多批任务,使得其他并发程序有机会将自己的运算插入到事件循环队列中交替运行,这就是并发协作。
以处理1000万条数据为例,我们采取并发协作的方式来实现:

var res = []; 
// response(..)从Ajax调用中取得结果数组
function response(data) { 
 // 一次处理1000个
 var chunk = data.splice( 0, 1000 ); 
 // 添加到已有的res组
 res = res.concat( 
 // 创建一个新的数组把chunk中所有值加倍
 chunk.map( function(val){ 
    return val * 2; 
 } ) 
 ); 
 // 还有剩下的需要处理吗?
 if (data.length > 0) { 
 // 异步调度下一次批处理
 setTimeout( function(){ 
    response( data ); 
 }, 0 ); 
 } 
} 
// ajax(..)是某个库中提供的某个Ajax函数
ajax( "http://some.url.1", response ); 
ajax( "http://some.url.2", response );
复制代码

我们每次处理1000条数据,然后将剩余数据通过定时器方式再加入事件循环队列末尾,好让其他程序有执行的机会,不断反复,就达到了交替执行。

任务

​ 在ES6中,有一个新的概念建立在事件循环队列之上,叫做任务队列(job queue),这个概念用于Promise的异步特性。

事件循环队列类似于一个游乐园游戏:玩过了一个游戏之后,你需要重新到队尾排队才能再玩一次。而任务队列类似于玩过了游戏之后,插队接着继续玩。

​ 任务队列是挂在前文提到的事件循环队列的每个tick之后的一个队列,但是不是被添加到队列末尾,而可以直接插队,优先处理,也即:”尽可能早的将来“

语句顺序

​ 代码中语句的顺序和JS引擎执行语句的顺序并不一定要一致。JS引擎在编译代码之后,可能会对语句的顺序进行重新安排,以提高执行速度。可以保证的是,JS引擎在编译阶段执行的优化都是安全的优化。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

你不知道的JavaScript---异步:现在与未来 的相关文章

随机推荐

  • GTest的测试环境搭建

    一 gtest的安装 Google test是一种比较方便的C 测试框架 它能够帮助我们比较方便的进行测试代码的编写 以及输出尽可能详细的失败信息 能够大大缩短我们测试代码的编写效率 而且该框架的使用方法也比较简单 能够降低我们学习新框架的
  • Java 移除重复节点

    移除重复节点 难度简单97 编写代码 移除未排序链表中的重复节点 保留最开始出现的节点 示例1 输入 1 2 3 3 2 1 输出 1 2 3 示例2 输入 1 1 1 1 2 输出 1 2 提示 链表长度在 0 20000 范围内 链表元
  • MES管理系统对电子企业来说有什么优点

    引言 在电子制造企业中 MES管理系统已经成为提高生产效率 降低成本 提高订单履行速度和准确性的重要工具 电子企业MES管理系统是一套集成的信息系统 用于监控和控制电子企业的生产过程 本文将探讨MES管理系统对于电子企业来说有哪些优点 一
  • 人工智能的最新进展:2024年将会发生什么?

    文章目录 2024年AI最新发展 2024年AI具体应用 2024年AI的具体预测 创作者 全栈弄潮儿 个人主页 全栈弄潮儿的个人主页 个人社区 欢迎你的加入 全栈弄潮儿的个人社区 专栏地址 AI大模型 人工智能 AI 是一种快速发展的技术
  • C++ MAP的遍历顺序和插入元素顺序是不同的

    当你为MAP插入一个元素后 MAP会按KEY的顺序重新排列 所以当你遍历MAP的时候 遍历的顺序已经不是你插入元素的顺序 举个具体例子 MAP B 1 MAP C 2 MAP A 3 当你遍历MAP输出的时候 是按 A B C 顺序输出的
  • Zookeeper和Nacos的区别

    目录 Zookeeper 1 ZK结构 2 ZK的消息广播和崩溃恢复 Nacos 1 存储和数据更新 2 注册中心 Zookeeper 1 ZK结构 Zookeeper的功能主要是通过它的树形节点来实现的 当有节点数据变化时或者说节点过期的
  • Dropout Learning - 防止深度神经网络过拟合

    最近在学习caffe 里面有一个名词叫做Dropout Learning 一直没明白是什么意思 直到最近才发现一片文章介绍Dropout Learning的 希望可以给不知道的同学一定的帮助 如果想要更深入的了解可以阅读该文献 文章结尾会给
  • MOV指令在32位汇编程序和64位汇编程序下的相同与不同之处

    mov指令原则 两个操作数 目标操作数和源操作数 的大小必须相同 两个操作数不能同时为内存操作数 也就是不能内存 到 内存 指令指针寄存器不能作为目标操作数 64位汇编程序下 32位汇编程序和64位汇编程序都依照上面的规则 语法也相同 但如
  • 目标跟踪算法

    目标跟踪算法 一 目标跟踪算法简介 1 1 主要任务 1 1 1 Online Visual Tracker BenchMark 1 1 2 VOT 1 2 难点与挑战 1 3 分类 1 3 1 常规分类 1 3 2 时间分类 二 常用算法
  • 从Bengio的NPS模型看AGI的实现通路

    来源 混沌巡洋舰 这两天深度学习祖师Yoshua Bengio 的 Neural Production System 刷新了AI圈子 与以往的深度学习套路不同的是 这篇文章有效的把符号主义AI对人类认知的模拟与深度学习结合 得到了一个能够学
  • 《每日一题》NO.21:画出CMOS 非门/与非门/或非门的结构

    芯司机 每日一题 会每天更新一道IC面试笔试题 其中有些题目已经被很多企业参考采用了哦 聪明的你快来挑战一下吧 今天是第21题 CMOS Complementary Metal Oxide Semiconductor 互补金属氧化物半导体
  • Vivo 2019秋季校园招聘笔试题(9月22号机考)

    Vivo笔试题这次真是出乎意料了 上来就直接三道编程题奉上 题目描述 1 小V在公司负责游戏运营 今天收到一款申请新上架的游戏 跳一跳 为了确保提供给广大玩家朋友们的游戏都是高品质的 按照运营流程小V必须对新游戏进行全方位了解体验和评估 这
  • webpack多个Html,Webpack构建多页面应用配置

    安装依赖 devDependencies babel core 7 12 3 babel preset env 7 12 1 babel preset react 7 12 1 babel loader 8 1 0 clean webpac
  • 一元多项式的相加

    一元多项式的表达和相加 使用单链表表示一元多项式 由于使用java语言自己编写实现 没有使用LinkedList集合实现 所以需要创建单链表的类 类中包含指数 系数和后继元素的地址 类的设计如下 public class SingleLis
  • 01背包问题(采药) 动态规划,python实现

    采药问题 题目描述 辰辰是个天资聪颖的孩子 他的梦想是成为世界上最伟大的医师 为此 他想拜附近最有威望的医师为师 医师为了判断他的资质 给他出了一个难题 医师把他带到一个到处都是草药的山洞里对他说 孩子 这个山洞里有一些不同的草药 采每一株
  • python后端接口框架Flask的基本用法

    简介 在现代Web开发中 后端接口是十分重要的一部分 它们建立了前端和后端之间的连接 使得数据能够在两者之间传递 Python是一门受欢迎的动态编程语言 它可以用来编写高效且功能强大的后端接口 本文将介绍如何使用Python编写后端接口 以
  • WSS 代码执行的权限提升

    WSS 代码执行的权限提升 概述 WSS 默认使用身份模拟执行代码 也就是说用当前登录的用户身份执行Web Part或者自定义应用程序的代码访问 在大多数情况下 这种机制能够准确并严格地控制了标准权限的用户他对特定网站资源和敏感数据的访问
  • [LeetCode-35]-Search Insert Position(搜索整数所在的位置)

    文章目录 题目相关 Solution 1 顺序遍历 2 算法改进 二分查找 题目相关 题目解读 从有序整数列表中搜索给定整数 如果在其中返回下标位置 如果不在 返回应该在的位置 题目 原题链接 Given a sorted array an
  • MyBatis3 映射boolean 类型注意事项

    1 MySQL8 数据库关于boolean 存储结构定义 使用tinyint 1 代表Boolean 类型 2 实体定义关于属性字段为boolean 类型定义 3 实体属性与数据库字段映射文件配置 Mapper xml 文件 4 控制层 如
  • 你不知道的JavaScript---异步:现在与未来

    目录 异步 分块的程序 事件循环 并行线程 并发 非交互 交互 协作 任务 语句顺序 异步 js如何表达和控制持续一段时间的程序行为 分散在一段时间内运行的程序行为 持续一段时间 不是指类似于 for循环开始到结束的过程 而是指 程序的一部