js异步队列和setTimeout

JS的异步机制由事件循环和任务队列构成.JS本身是单线程语言,所谓异步依赖于浏览器或者操作系统等完成. JavaScript 主线程拥有一个执行栈以及一个任务队列,主线程会依次执行代码,当遇到函数时,会先将函数入栈,函数运行完毕后再将该函数出栈,直到所有代码执行完毕。

JavaScript是单线程执行的,无法同时执行多段代码。当某一段代码正在执行的时候,所有后续的任务都必须等待,形成一个队列。一旦当前任务执行完毕,再从队列中取出下一个任务,这也常被称为 “阻塞式执行”。所以一次鼠标点击,或是计时器到达时间点,或是Ajax请求完成触发了回调函数,这些事件处理程序或回调函数都不会立即运行,而是立即排队,一旦线程有空闲就执行。假如当前 JavaScript线程正在执行一段很耗时的代码,此时发生了一次鼠标点击,那么事件处理程序就被阻塞,用户也无法立即看到反馈,事件处理程序会被放入任务队列,直到前面的代码结束以后才会开始执行。如果代码中设定了一个 setTimeout,那么浏览器便会在合适的时间,将代码插入任务队列,如果这个时间设为 0,就代表立即插入队列,但不是立即执行,仍然要等待前面代码执行完毕。所以 setTimeout 并不能保证执行的时间,是否及时执行取决于 JavaScript 线程是拥挤还是空闲。

简单异步问题

在JavaScript的世界中,所有代码都是单线程执行的,由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现

    window.setTimeout(function(){
        console.log(123)
    }, 1000)

同样异步问题会出现在 Ajax 请求当中

 $.ajax({
        url: '',
        success: function(res){
            generateTable(res.data);
        }
    })

    function generateTable(data){
        console.log(data);
    }

复杂异步问题

在很多开发场景当中会有比较多的请求需要按顺序串行执行,比如有 var apis = ['api1', 'api2', 'api3'],需求是需要按顺序执行,先完成请求一再发起请求二然后再是请求三,这个时候用 Ajax 请求就只能是回调嵌套回调

$.ajax({
        url: 'api1',
        success: function(res){
            $.ajax({
                url: 'api2',
                success: function(res){
                    $.ajax({
                        url: 'api3',
                        success: function(res){
                            generateTable(res.data);
                        }
                    })
                }
            })
        }
    })

    function generateTable(data){
        console.log(data);
    }

以上的解决方案可行,但如何 api 有 n 个,那这种无多层嵌套的方式我们称之为回调地狱

Promise

为了解决上述回调地狱问题,便推出了 Promise 并在 ES6 中被统一规范,由浏览器直接支持。

Promise 简单使用

Promise 是一个构造函数,接收一个 function 为参数,该回调函数有会被传两个形参 resolve 和 reject,resolve 用于执行正常结果,并用 then 来接收

var p1 = new Promise(function(resolve, reject){
        window.setTimeout(function(){
            resolve(123)
        }, 1000);
    })

    p1.then(function(res){
        console.log(res)
    })

用 reject 用来执行异常结果。用 catch 来获取异常信息

var p1 = new Promise(function(resolve, reject){
        window.setTimeout(function(){
            reject('error')
        }, 1000);
    })

    p1.then(function(res){
        console.log(res)
    }).catch(function(error){
        console.log(error)
    })

状态不可逆性、链式调用(串行调用)

如上述如何有多个异步需要串行执行,用 Promise 实现会简单很多

var p = new Promise(function(resolve, reject){  
    window.setTimeout(function(){
        resolve(1);  
    }, 1000)
  });  
  p.then(function(value){               //第一个then  
    console.log(value);  
    return value*2;  
  }).then(function(value){              //第二个then  
    console.log(value);  
  }).then(function(value){              //第三个then  
    console.log(value);  
    return Promise.resolve('resolve');   
  }).then(function(value){              //第四个then  
    console.log(value);  
    return Promise.reject('reject');  
  }).then(function(value){              //第五个then  
    console.log('resolve: '+ value);  
  }, function(err){  
    console.log('reject: ' + err);  
  })  

小结

  1. 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态
    • pending: 初始状态,不是成功或失败状态
    • fulfilled: 意味着操作成功完成
    • rejected: 意味着操作失败
  2. Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了。
new Promise(function(resolve, reject){
    window.setTimeout(function(){
        reject('error')
    }, 2000)
 
    window.setTimeout(function(){
        resolve('ok')
    }, 3000)


}).then(function(res){
    console.log(res)
}).catch(function(error){
    console.log(error)
})
  1. 优点:有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

  2. 缺点:

    • 首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消.
    • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部
    • 当处于 Pending 状态时,无法得知目前进展到哪一个阶段
  3. 规范

    • Pending – Promise对象的初始状态,等到任务的完成或者被拒绝;Resolved – 任务执行完成并且成功的状态;Rejected – 任务执行完成并且失败的状态;
    • Promise的状态只可能从Pending状态转到Resolved状态或者Rejected状态,而且不能逆向转换,同时Resolved状态和Rejected状态也不能相互转换;
    • Promise对象必须实现then方法,then是promise规范的核心,而且then方法也必须返回一个Promise对象,同一个Promise对象可以注册多个then方法,并且回调的执行顺序跟它们的注册顺序一致;
    • then方法接受两个回调函数,它们分别为:成功时的回调和失败时的回调;并且它们分别在:Promise由Pending状态转换到Resolved状态时被调用和在Promise由Pending状态转换到Rejected状态时被调用。

Promise.all

有时候需要使用并行执行时可以用 Promise.all,then 返回的时间以最后一个异步执行的完成时间为准。

    var promise1 = Promise.resolve(3);
    var promise2 = 42;
    var promise3 = new Promise(function(resolve, reject) {
        setTimeout(resolve, 2000);
    });

    Promise.all([promise1, promise2, promise3]).then(function(values) {
        console.log(values);
    });

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值