ES6|微任务—promise


Promise是ES6提供的新API,也引出了新的概念——微任务,在讲Promise之前,有必要补充一下JS引擎执行过程的相关知识。

JS的执行阶段

js引擎的执行阶段分为三个部分:语法分析—>预编译—>执行阶段,这里来讲讲第三个阶段——执行阶段。下面通过概念理解来一步步理解执行阶段。

  1. js是单线程的
    单线程的概念就是同一时间只能做一件事,多线程则是同一时间可以做多件事,而js作为浏览器脚本语言,对DOM操作是种很常见的操作,试想,如果js是多线程的,那同时执行了对一个dom的增加又删除,那岂不是会出错乱,由此可见,js是单线程是有它自己的理由的。但是,我们知道,在进行前端渲染的过程中,如果遇到了js脚本就会停止渲染,先执行js,等到js执行完了才继续渲染,这时候,如果js的所有任务都排着队一个个去执行,岂不是会造成阻塞,使得网页加载时间过长?
  2. 宏任务
    就上面那个问题,js利用宏任务来解决这个问题。宏任务又分同步任务和异步任务。讲宏任务之前,先来讲讲网页的线程有:
  • JS引擎线程: 也称为JS内核,负责解析执行Javascript脚本程序的主线程(例如V8引擎)

  • 事件触发线程:
    归属于浏览器内核进程,不受JS引擎线程控制。主要用于控制事件(例如鼠标,键盘等事件),当该事件被触发时候,事件触发线程就会把该事件的处理函数推进事件队列,等待JS引擎线程执行

  • 定时器触发线程:主要控制计时器setInterval和延时器setTimeout,用于定时器的计时,计时完毕,满足定时器的触发条件,则将定时器的处理函数推进事件队列中,等待JS引擎线程执行。

  • 异步请求线程:通过XMLHttpRequest连接后,通过浏览器新开的一个线程,监控readyState状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推进事件队列中,等待JS引擎线程执行。
    以上四个线程只有主线程在执行,其他三个线程在满足触发条件时会被推进时间队列,等待js引擎执行。可以结合下面这张图来理解:


图片描述-------------------------------------------------------------------------
现在回到我们的宏任务,js会判断任务为同步任务还是异步任务,同步任务会被推到主线程,异步任务不直接进入主线程,而是在满足一定触发条件之后,被推到事件队列中,等待主线程上所有同步任务执行完之后,主线程再从异步任务队列中拉取任务挨个执行。注意:这里强调的是,所有同步任务执行完了之后才会开始执行异步任务。 这种执行顺序就叫事件循环(event loop),事件循环是js的执行机制。

说了这么多,下面看一个简单例子来理解js的执行机制:

console.log(1)
setTimeout(() => { // 异步任务,放入任务队列中
    console.log(2)
}, 0)
console.log(3)
//输出顺序分别为1 3 2
  1. 微任务(micro-task)
    微任务是在es6和node环境中出现的一个任务类型,如果不考虑es6和node环境的话,我们只需要理解宏任务事件循环的执行过程就已经足够了,但是到了es6和node环境,我们就需要理解微任务的执行顺序了。 微任务的API主要有:Promise,process.nextTick,在这里我们就只讲Promise。
    Promise本身是同步任务,但是执行resolve或者reject回调的时候,此时是异步操作,会先将then/catch放到微任务队列。当主栈完成后,才会再去调用resolve/reject方法执行。(这里看不懂没关系,往下看,留个印象)
    慢慢来。先来看一下宏任务与微任务的联系:

在这里插入图片描述-----------------------------------------------------------------------------------

这张图可以理解为js执行顺序为:同步任务—>微任务—>异步任务
(之后会结合微任务和Promise一起讲)

补充:setTimeout和setInterval的区别:

setTimeout是在到了指定时间的时候就把事件推到任务队列中,只有当在任务队列中的setTimeout事件被主线程执行后,才会继续再次在到了指定时间的时候把事件推到任务队列,那么setTimeout的事件执行肯定比指定的时间要久,具体相差多少跟代码执行时间有关

setInterval则是每次都精确的隔一段时间就向任务队列推入一个事件,无论上一个setInterval事件是否已经执行,所以有可能存在setInterval的事件任务累积,导致setInterval的代码重复连续执行多次,影响页面性能。

Promise

Promise,可以理解为一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,Promise对象有以下两个特点:

  • 对象的状态不受外界影响。Promise对象有三种状态:pending(进行中)、Fullfilled(已成功)和Rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,仍和其他操作都无法改变这个状态
  • 一旦状态改变就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变只有两种可能:Pending—>Fulfilled、Pending—>Rejected。只要这两种情况有一种发生,状态就凝固了不会再变了,而是一直保持这个结果,这时就称为Resolved(已定型)。为了行文方便,后续的Resolved统一指的是Fulfilled状态。

基本用法

let promise = new Promise((resolve,reject) => {
    // ...some code
    if(/*异步操作成功*/){
        resolve(value)
    }else{
        reject(error)
    }   
}).then((value) => {/*success*/}, (error) => {/*error*/})

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject两个函数,由js引擎提供,不用自己部署。

resolve将Promise的状态从Pending—>Resolved,reject将Promise的状态从Pending—>Rejected,当状态变成Resolved时,会执行then的第一个函数参数,并将resolve函数的参数传进去(value),当状态变成Rejected时,会执行then的第二个函数参数,并将reject的函数的参数传进去(error)。

Promise对象生成后,可以用then方法分别指定Resolved和Rejected状态的回调函数:

let p=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log('hello')
        resolve('成功')//promise状态不可逆
        reject('失败')
    },1000)
}).then((res)=>{
    console.log(res)
},(err)=>{
    console.log(err)
})

//成功

也可以用then指定Resolved的回调函数,用catch指定Rejected的回调函数:

let p=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log('hello')
        resolve('成功')//promise状态不可逆
        reject('失败')
    },1000)
}).then((res)=>{
    console.log(res)
}).catch((error) => {
	console.log(error)
})

//成功

关于输出问题

之前有说到,Promise构造函数本身是同步任务,会直接进入js主线程,但是Promise的回调函数then/catch会被推到微任务队列,而js的执行机制是:同步任务—>微任务—>异步任务 ,下面结合简单例子再来理解一下。

let p=new Promise((resolve,reject)=>{
    console.log(1)
})//同步
console.log(2)//同步
p.then(()=>{
    console.log(3)
})//微任务

//1 2 3

以上,Promise构造函数本身是同步任务,console.log(2)无疑也是同步任务,它们会按照顺序在主线程执行,而p.then这个回调函数是微任务,会被推到微任务队列,待主线程所有同步任务执行完了之后才会执行这个微任务。

//同步任务
let p1=new Promise((resolve,reject)=>{
    resolve(1)
})
let p2=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve(2)
    },1000)
})
let p3=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        reject(3)
    },1000)
})
console.log(p1)
console.log(p2)
console.log(p3)
//异步任务
setTimeout(()=>{
    console.log(p2)
},2000)
setTimeout(()=>{
    console.log(p3)
},2000)
//微任务
p1.then((res)=>{
    console.log(res)
})
p2.then((res)=>{
    console.log(res)
})
p3.catch(err=>{
    console.log(err)
})


输出顺序为:

//Promise {<resolved>: 1}
//Promise {<pending>}
//Promise {<pending>}
//1
//2
//3
//Promise {<resolved>: 2}
//Promise {<rejected>: 3}

在这里插入图片描述

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值