JavaScript异步精讲,让你更加明白Js的执行流程!

简介: JavaScript异步精讲,让你更加明白Js的执行流程! 问题点 什么是单线程,和异步有什么关系 什么是 event-loop jQuery的Deferred Promise 的基本使用和原理 async/await(和 Promise的区别、联系) 一、什么是单线程,和异步有什么关系 单线程.

JavaScript异步精讲,让你更加明白Js的执行流程!

问题点

  • 什么是单线程,和异步有什么关系
  • 什么是 event-loop
  • jQuery的Deferred
  • Promise 的基本使用和原理
  • async/await(和 Promise的区别、联系)

一、什么是单线程,和异步有什么关系

  • 单线程- 只有一个线程,只能做一件事
  • 原因-避免DOM渲染的冲突
  • 解决方案-异步

1) 单线程- 只有一个线程,只能做一件事

基础事例

// 循环运行期间,JS执行和DOM 渲染暂时卡顿
var i, sum = 0;
for (i = 0; i<100000000000; i++) {
  sum += i;
}
console.log(sum);

// alert不处理,JS执行和DOM 渲染暂时卡顿
console.log(1);
alert('hello');
console.log(2)

2) 原因 - 避免DOM渲染的冲突

  1. 浏览器需要渲染DOM
  2. JS可以修改DOM结构
  3. 所以JS执行的时候,浏览器 DOM 渲染会暂停
  4. 两段JS也不能同时执行(都修改 DOM 就冲突了)
  5. webworker支持多线程,但是不能修改访问 DOM

3) 解决方案 - 异步

基础事例

console.log(100)

setTimeout(function () {
  console.log(200); // 反正1000ms之后执行
}, 1000);           // 先不管他,先让其它 JS 代码运行

console.log(300);
console.log(400);

4) 异步 - 存在的问题

问题一:没有按照书写方式执行,可读性差
问题二:callback中不容易模块化

二、什么event-loop

文字解释

  1. 事件轮询,JS实现异步的具体解决方案
  2. 同步代码,直接执行
  3. 异步函数先放在异步队列中
  4. 待同步函数执行完毕,轮询执行异步队列函数

例子分析

例子一:

clipboard.png

先看左下角,是我们要执行的代码,第一个是延迟100毫秒打印1,第二个是延迟0,打印2,按照上面讲的,同步代码先执行,异步代码放在异步队列中,最后执行完同步在执行异步队列的函数,执行第一个函数的时候,setTimeout是个异步函数,那我们是不是应该立即放在右侧的队列中呢?答案是否定的,因为它有一个延时,我们需要100毫秒之后才能放进去,执行第二个setTimeout函数,由于没有延时,所以会被放进异步队列中,最后一个是直接 在主进程,所以 直接执行打印,等同步执行完之后,立刻看异步队列中,这时候只有第二个setTimeout,执行完之后,在去看异步队列有没有,因为100这毫秒对于计算机来说,是一个相当长的时间,所以js引擎会一直轮询异步队列中有没有可执行函数,直接100毫秒之后第一个setTimeout被放进异队列中,然后才执行。

例子一:

clipboard.png
上图流程跟例子一一样,这边我们疑惑的是,当我们执行 ajax 的时候,里面的success 是异步函数,它要什么时候被放进异步队列中呢?
当然是请求成功的时候被放进异步队列中,但我们不知道是 大于100毫秒还是小于,所以打印结果有两种情况,先打印d c b a,或者打印 d c a b。

三、是否用过jQuery的Deferred

jQuery 1.5的变化 -1.5之前

clipboard.png

jQuery 1.5的变化 -1.5之后

clipboard.png

jQuery 1.5的变化

无法改变JS异步和单线程的本质
只能从写法上杜绝 callback 这种形式
它是一种语法糖形式,但是解藕了代码
很好的体现:开放封装原则

1) 什么是deferred对象?

开发网站的过程中,我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。
通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。

但是,在回调函数方面,jQuery的功能非常弱。为了改变这一点,jQuery开发团队就设计了deferred对象。
简单说,deferred对象就是jQuery的回调函数解决方案。

2) deferred应用

我们来看一个具体的例子。假定有一个很耗时的操作wait:

var wait = function() {
  var tasks = function() {
    alert('执行完毕');
  };
  setTimeout(tasks,5000);
 }
 

我们为它指定回调函数,应该怎么做呢?

你可能会使用$when()

 $.when(wait())
 .done(function(){
   alert('成功!')
 })
 .fail(function() {
   alert('出错!');
 })
 

但是,这样写的话,done()方法会立即执行,起不到回调函数的作用。原因在于$.when()的参数只能是deferred对象,所以必须对wait()进行改写:

var dtd = $.Deferred(); //新建一个deferred

var wait = function (dtd) {
  var tasks = function () {
    console.log('执行完毕');
    dtd.resolve(); //改变deferred对象执行状态
  };

  setTimeout(tasks, 5000);

  return dtd;
}

现在,wait()函数返回的是deferred对象,这就可以加上链式操作了。

$.when(wait(dtd))

.done(function(){ alert("成功!"); })

.fail(function(){ alert("出错!"); });

更多内容可参考这里

四、Promise 的基本使用和原理

1) 什么是 Promise

一人Promise 对象代表一个目前还不可用,但是在未来的某个时间点可以被解析的值。它允许你以一种同步的方式编写异步代码。Promise的出现,原本是为了解决回调地狱的问题。所有人在讲解Promise时,都会以一个ajax请求为例,此处我们也用一个简单的ajax的例子来带大家看一下Promise是如何使用的。

ajax请求的传统写法:

getData(method, url, successFun, failFun){
  var xmlHttp = new XMLHttpRequest();
  xmlHttp.open(method, url);
  xmlHttp.send();
  xmlHttp.onload = function () {
    if (this.status == 200 ) {
      successFun(this.response);
    } else {
      failFun(this.statusText);
    }
  };
  xmlHttp.onerror = function () {
    failFun(this.statusText);
  };
}

改为promise后的写法:

getData(method, url){
  var promise = new Promise(function(resolve, reject){
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open(method, url);
    xmlHttp.send();
    xmlHttp.onload = function () {
      if (this.status == 200 ) {
        resolve(this.response);
      } else {
        reject(this.statusText);
      }
    };
    xmlHttp.onerror = function () {
      reject(this.statusText);
    };
  })
  return promise;
}

getData('get','www.xxx.com').then(successFun, failFun)

很显然,我们把异步中使用回调函数的场景改为了.then()、.catch()等函数链式调用的方式。基于promise我们可以把复杂的异步回调处理方式进行模块化。

2)Promise的原理分析

其实 promise 原理说起来并不难,它内部有三个状态,分别是 pending, fulfilled 和 rejected。

peding 是对象创建后的初始状态,当对象fulfill(成功)时变为 fulfilled, 当对象 reject(失败)时变为 rejected。且只能从 pending 变为 fulfilled 或 rejected,而不能逆向或或从 fulfilled 变为rejected,从 rejected变为 fulfilled。如图所示:


相关文章
|
5天前
|
前端开发 JavaScript 区块链
连接区块链节点的 JavaScript 库 web3.js
连接区块链节点的 JavaScript 库 web3.js
|
16天前
|
前端开发 JavaScript UED
深入理解 JavaScript 同步和异步,让网页灵动起来!
深入理解 JavaScript 同步和异步,让网页灵动起来!
|
16天前
|
JavaScript 前端开发 Go
动态加载与异步加载 JavaScript 详解:加载远程js,加载成功后执行回调函数
动态加载与异步加载 JavaScript 详解:加载远程js,加载成功后执行回调函数
|
3天前
|
JavaScript Java 测试技术
基于springboot+vue.js的工作流程管理系统附带文章和源代码设计说明文档ppt
基于springboot+vue.js的工作流程管理系统附带文章和源代码设计说明文档ppt
7 0
|
9天前
|
JavaScript 前端开发
JavaScript-jQuery的使用 + JS的案例
JavaScript-jQuery的使用 + JS的案例
16 0
|
16天前
|
JavaScript 前端开发
深入理解Vue.js中的nextTick:实现异步更新的奥秘
深入理解Vue.js中的nextTick:实现异步更新的奥秘
|
17天前
|
移动开发 JavaScript 前端开发
Phaser和Three.js是两个非常流行的JavaScript游戏框架
【5月更文挑战第14天】Phaser是开源的2D游戏引擎,适合HTML5游戏开发,内置物理引擎和强大的图形渲染功能,适用于消消乐等2D游戏。Three.js是基于WebGL的3D库,用于创建3D场景和应用,支持各种3D对象和交互功能,广泛应用于游戏、可视化等领域。选择框架取决于项目需求,2D选Phaser,3D选Three.js。
18 4
|
17天前
|
前端开发 JavaScript UED
由于JavaScript是单线程的,因此在处理大量异步操作时,需要确保不会阻塞UI线程
【5月更文挑战第13天】JavaScript中的Promise和async/await常用于处理游戏开发中的异步操作,如加载资源、网络请求和动画帧更新。Promise表示异步操作的结果,通过.then()和.catch()处理回调。async/await作为Promise的语法糖,使异步代码更简洁,类似同步代码。在游戏循环中,使用async/await可清晰管理资源加载和更新,但需注意避免阻塞UI线程,并妥善处理加载顺序、错误和资源管理,以保证游戏性能和稳定性。
25 3
|
17天前
|
前端开发 JavaScript 数据处理
在JavaScript中,异步函数是指什么
【5月更文挑战第9天】JavaScript中的异步函数用于处理非立即完成的操作,如定时器、网络请求等。它们可通过回调函数、Promise或async/await来实现。示例展示了如何使用async/await模拟网络请求:定义异步函数fetchData返回Promise,在另一异步函数processData中使用await等待结果并处理。当fetchData的Promise解析时,data变量接收结果并继续执行后续代码。注意,调用异步函数不会阻塞执行,而是会在适当时间点继续。
17 0
|
17天前
|
JSON JavaScript 前端开发
使用JavaScript和XLSX.js将数据导出为Excel文件
使用JavaScript和XLSX.js将数据导出为Excel文件
36 0