1.应用场景
学习弄清楚Js的一些重要概念, 对前端项目开发是极为重要的~~ |
2.学习/操作
Js中多线程-单线程, 同步-异步,阻塞-非阻塞, 回调函数的关系理解 //2018.12.12 提前说一句, 他们几者之间是有因果关系的.
1.多线程/单线程 多线程: 程序同一时间可以做几件事. 单线程: 程序同一时间只能做一件事.
在JavaScript的世界中,所有代码都是单线程执行的. JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。 这决定了它只能是单线程,否则会带来很复杂的同步问题。
比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准? 所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变.
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制, 且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。//但是一些不涉及到dom操作的操作, 如复杂的计算等, 可以考虑新开线程去做,减少主线程占用时间, 且可以快速渲染结果.
即通常情况下, Js中不必考虑多线程.
补充 Js单线程: 在浏览器的一个页面中,该页面的的Js只有一个master主线程[Js脚本运行在上面](注意:Js是单线程,但浏览器内部并不是单线程,I/O、定时器、事件监听等都是浏览器的其他线程完成的),所以叫单线程。 因为Js是单线程,所以程序的执行顺序都是从上到下依次进行的,同一时间内只能有一段代码被执行。
2.同步/异步 同步任务和异步任务 程序里面所有的任务,可以分成两类:同步任务(synchronous)和异步任务(asynchronous)。 同步任务是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。 异步任务是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。排在异步任务后面的代码,不用等待异步任务结束会马上运行,也就是说,异步任务不具有“堵塞”效应。
举例来说,Ajax 操作可以当作同步任务处理,也可以当作异步任务处理,由开发者决定。 如果是同步任务,主线程就等着 Ajax 操作返回结果,再往下执行; 如果是异步任务,主线程在发出 Ajax 请求以后,就直接往下执行,等到 Ajax 操作有了结果,主线程再执行对应的回调函数。
补充: 这里的同步/异步并不是其字面意思.
同步: 程序开始执行和执行结束的顺序和排队的顺序是一定是相同的.[因为是单线程,就引出了队列的概念,必须要排队.不管怎样,就是排队, 一个一个来,不允许插队] 也就是前一个程序执行完, 后一个程序才能执行, 否则就后者就一直处于等待过程中, 就引出了阻塞概念. 耗时过长的阻塞会拖延整个程序的执行。常见的浏览器无响应(假死).
异步: 程序执行结束的顺序和排队的顺序是不相同的. 后者不等待前者执行完毕, 就可以开始执行. 异步编程的的本质目的就是为了提高cpu的执行时间.
其实同步和异步,无论如何,做事情的时候都是只有一条流水线(单线程 [Js就是单线程的,不会变]), 同步和异步的差别就在于这条流水线上各个流程的执行顺序不同。
Js中,最基础的异步是setTimeout和setInterval函数,很常见,但是很少人有人知道其实这就是异步,因为它们可以控制Js的执行顺序.
3.阻塞/非阻塞 如字面意思一样. 阻塞: 就是程序处于等待过程中, 必须等待当前程序执行完毕, 后面程序才能执行. 对于要求速度和效率的web程序,这明显是不可行的. 非阻塞: 程序不必等待前一个程序执行完, 就能执行, 这正是Js想达到的目的
同步带来的问题, 就是如果程序中有耗时较长的操作, 就会造成阻塞. 所以Js中耗时操作, 如:JavaScript的所有网络操作,浏览器事件,都必须是异步执行, 异步操作, 就引出了下面的问题5(回调函数), 回调函数只是实现异步编程的几种方式之一
常见的异步编程有: 回调函数, 事件监听, 发布/订阅, Promise对象 还有ES7发布的async与await 详情参考: http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html
Promise: //解决的只是异步编程风格的问题, 避免回调地狱. 简单介绍: 古人云:“君子一诺千金”,这种“承诺将来会执行”的对象在JavaScript中称为Promise对象。 Promise有各种开源实现,在ES6中被统一规范,由浏览器直接支持。
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
详细参见: https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434 5008539155e93fc16046d4bb7854943814c4f9dc2000 // 廖雪峰 http://es6.ruanyifeng.com/#docs/promise //阮一峰
补充: 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。于是就有一个概念,任务队列。
这里涉及到主线程和任务队列两个概念.暂不详述. //任务队列又涉及到宏任务与微任务队列, 深入可以另行参考资料.
4.回调函数 这是异步编程最基本的方法。 简单理解为: 类似于迂回战略, 先去做, 短时间内可能看不到响应, 但是一旦有响应, 就"立即"继续操作. //这里的立即, 不是绝对的立即, 也是要在主线程上的当前任务执行完毕之后, 才会执行. 所以也是有延迟等待的[阻塞]
5.串行执行异步任务/并行执行异步任务 详情参见: https://wangdoc.com/javascript/async/general.html // 阮一峰 - 异步操作概述 属于流程控制.
串行执行异步任务: 按照顺序执行, 前后有依赖关系. 类似于接力赛. 比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续, 并执行错误处理函数。
我们可以编写一个流程控制函数,让它来控制异步任务,一个任务完成以后,再执行另一个。这就叫串行执行。 code:
Note: 上面代码中,函数 注意,上面的写法需要6秒,才能完成整个脚本。//执行的时候, 可以看到控制台每秒打印一个.
并行执行异步任务: 也是按照顺序执行[因为单线程], 但是任务之间是并列关系, 之间并无依赖关系. 类似于预装东西 试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的. 有些时候,多个异步任务是为了容错。 比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可.
流程控制函数也可以是并行执行,即所有异步任务同时执行,等到全部完成以后,才执行 code:
上面代码中, 相比而言,上面的写法只要一秒,就能完成整个脚本。这就是说,并行执行的效率较高,比起串行执行一次只能执行一个任务,较为节约时间。但是问题在于如果并行的任务较多,很容易耗尽系统资源,拖慢运行速度。因此有了第三种流程控制方式。
Note: 上面的写法需要1秒,便完成整个脚本。执行时, 可以看到控制台1秒打印完毕.
并行与串行的结合 所谓并行与串行的结合,就是设置一个门槛,每次最多只能并行执行
上面代码中,最多只能同时运行两个异步任务。变量
Note: 这段代码需要3秒完成整个脚本,处在串行执行和并行执行之间。通过调节
6. 总结 几者之间的关系如下:
最后个人的理解: 在Js实际开发中,并不会只有同步模式或者异步模式, 而尝尝是两者交叉使用或者说不存在完全异步编程的情况.
Question: 有没有可能出现只有异步的程序,这样的程序会出现什么问题? Answer: 同步异步本身就是来区分程序的执行依赖,是在前面的执行结束后在执行 或者不关注前面程序的的执行结果。 但归根结底 先后顺序 或者说 同步 一定是存在的.
备注: 任何学习, 某种程度上都是参考并思考的过程, 所以建议:大胆参考, 认真思考, 小心求证. |
3.问题/补充
1.理解还是有些不清晰. 暂时的解决办法是,多实践前端开发, 代码写的多了, 理解自然就会多一些. 20200412 公寓
理解思考记录: 2018-11-28 第一遍 2020-04-12 第二遍 2020-06-20 第三遍 |
4.参考
https://www.cnblogs.com/c3gen/p/6170504.html 16046d4bb7854943814c4f9dc2000 https://wangdoc.com/javascript/async/index.html //阮一峰 - 异步操作 |
后续补充
...