js单线程与异步

本文探讨了JavaScript中的同步任务和异步任务,重点介绍了Promise对象及其特点和缺点,包括事件循环机制、setTimeout、Web Worker以及异步处理的挑战。通过示例解释了异步任务如何在主线程中执行,并分析了Web Worker的限制和Promise在管理异步操作中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

js中的代码,对应有同步任务和异步任务,同步任务会在js主线程中排队执行,异步任务会被挂起(即暂不执行),并在一个队列(task queue)中添加一个事件。

当这些同步任务执行完了(主线程空闲),js主线程会到队列中读取事件,然后在主线程中执行对应的异步任务。这个过程是一致不断的循环进行,所以又称事件循环机制(Event Loop)。


注意:队列中添加的是事件,每个事件对应一个任务,所以称这个队列为任务队列(task queue)。


那么,怎么设置异步任务呢?或者说哪些操作是异步的呢?

1.  鼠标点击等事件的回调函数。 

也就是说当用户点击时,点击事件会被添加到任务队列中去,主线程循环到这个事件时,对应的回调函数(异步任务)就会被执行(在主线程中);注意,事件注册并不是一个异步任务,即ele.onclick=function(){}只是注册了一个事件,只有当用户点击了(进入task queue),主线程循环到此事件,对应的function才会被执行。


2. setTimeout & setInterval

两种方法的作用是推迟异步任务(回调函数)进入队列的时间,并不能保证异步任务在指定的时间后执行。

比如:在js代码执行到setTimeout(function(){},5000)时,意思是“我(function)将在5s之后进入任务队列”。5s后,如果主线程空闲,那么function会立即执行,从外面来看,实现了“function被延迟5s执行”;如果主线程还在忙(可能是同步代码尚未执行完或还在执行前面的异步任务),那么function会等到主线程执行完了再执行,此时时间已经大于5s。 看下面的例子:

var start=new Date();

setTimeout(function () {
    console.log(new Date()-start);
},500);

while(new Date()-start<=1000){}
结果会输出一个大于1000的数字。

即便将指定时间置为0,setTimeout(function(){},0),function也不会立即执行,只是将任务“立即”(加引号是因为浏览器默认会有几毫秒~十几毫秒的延迟)插入到任务队列,任务的执行时刻同样受到(同步任务的执行时间+前面的异步任务执行时间)的影响。


3. web worker

先看一张图:(来自伯乐在线/TGCode



当在 HTML 页面中执行js脚本时,页面是无法响应用户操作的,直到脚本已完成。

web worker 是运行在后台的 JavaScript,独立于其他脚本,不占用浏览器线程,不会影响页面的性能。您可以继续做任何愿意做的事情:点击、选取内容等等,而此时 web worker 在后台运行。

注意: web worker并没有改变js引擎的单线程,只是不占用浏览器线程,且有以下限制:

  • Web Worker无法访问DOM节点;
  • Web Worker无法访问全局变量或是全局函数;
  • Web Worker无法调用alert()或者confirm之类的函数;
  • Web Worker无法访问window、document之类的浏览器全局变量;

不过Web Worker中的Javascript依然可以使用setTimeout(),setInterval()之类的函数,也可以使用XMLHttpRequest对象来做Ajax通信。

这些限制也很好理解,有关页面的操作只能在js线程中完成,因为如果其他线程也能进行页面操作,当操作发生冲突时,比如一个增加节点,一个是删除节点,就会造成页面混乱。


4. promise

参考文献:

大白话讲解Promise

Promise对象

promise将异步操作函数作为参数传入构造函数,提供了许多控制异步操作的方法。能够将异步操作以同步操作的流程表达出来,避免了回调函数的层层嵌套。

用法和接口总结如下:

// 首先将promise的构造过程封装在一个函数中,因为如果直接写new promise(function(){}),会直接发起异步操作,
// 封装起来可以在需要的时候再发起
function Aync() {
    let p=new Promise(
        function (resolve,reject) {
            let data;// 异步操作要返回的数据
            /*
            ...
            异步操作代码
            */
            resolve(data); // 如果操作成功,将结果数据通过resolve传递出去
            reject("对不起,操作失败!"); // 如果操作失败,将失败的信息通过resolve传递出去
            // 判断是否成功,可写在异步操作代码里面
        }
    );
    return p;   // 返回promise对象
}

Aync().
then(
    function(data){},   // 执行resolve的回调,处理resolve传递的异步操作返回值
    function(errinfo){} // 执行reject的回调,处理reject传递的失败信息
);

Aync().
then(
    function(data){},   // 执行resolve的回调,处理resolve传递的异步操作返回值

).catch(
    function(errinfo){} // 执行reject的回调,处理reject传递的失败信息,还可以处理resolve回调函数中的错误
);

//将多个Promise实例包装成一个新的Promise实例
let P=new Promise([p1,p2,p3]);
/*
 P的状态由p1p2p3决定,分成两种情况。

1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
*/

//将多个Promise实例包装成一个新的Promise实例,PP的状态由p1p2p3中最先改变的决定
let PP=new Promise([p1,p2,p3]);

Promise对象特点

  1. 对象的状态不受外界影响。
  2. 一旦状态改变,就不会再变

Promise对象缺点:

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值