js事件循环——看输出顺序

js事件循环

由于js是单线程的,同一时间只能干一件事情,当期宿主环境为浏览器时,若一个任务耗时过长会导致页面阻塞。因此有了js事件循环机制,它将任务分成同步任务和异步任务,同步任务在主线程不断执行,异步任务进入任务队列,当同步任务执行完主栈为空时,就去任务队列读取异步任务执行,这个不断循环的过程称为事件循环。

1.promise:

微任务,进入微任务队列,在本轮事件循环中执行,例如:

new Promise(function(resolve) {
    console.log('promise');
}).then(function() {
    console.log('then');
})

new Promise立即执行,then函数分发到微任务Event Queue。

需要注意的几点

1.promise.then用于指定promise对象成功或者失败的回调函数,所以仅当promise对象为fulfilled或者rejected状态才会去执行,否则不执行:

const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('success')
    }, 1000)
})
const promise2 = promise1.then(() => {
    throw new Error('error!!!')
})
 
console.log('promise1', promise1)
console.log('promise2', promise2)
 
setTimeout(() => {
    console.log('promise1', promise1)  
    console.log('promise2', promise2)
}, 2000)



执行结果是:

对于这里为什么throw错误之后代码还能继续执行:因为Promise对象和Promise.then方法内部其实自带了try catch,当同步代码发生运行时错误时,会自动将错误对象作为值reject,捕获错误,所以代码还能继续往下运行。此题promise1.then执行后返回一个被reject的promise对象。

 2.Promise.then返回值是一个promise对象,以实现链式调用。

(1)当回调函数返回值是一个promise对象时,得到新的promise值作为返回的promise结果值;

(2)若返回值是一个一般值时,将这个值作为返回的promise结果值,相当于resolve(x),可继续向下一个then中的回调去传递;

(3)若不显式的 return 则会 return 一个以 undefined 值 resolve 的 Promise 对象

Promise.resolve(1)
    .then((res) => {
        console.log(res)
        return 2
    })
    .catch((err) => {
        return 3
    })
    .then((res) => {
        console.log('执行了');
        console.log(res)
    })

依次输出:1,执行了,2

3.promise有pending,fulfilled,rejected三种状态,状态只能被改变一次

const promise = new Promise((resolve, reject) => {
    resolve('success1')
    reject('error')
    resolve('success2')
})
promise
.then((res) => {
    console.log('then: ', res)
})
.catch((err) => {
    console.log('catch: ', err)
})

代码输出:success1,then:success1

2.setTimeout:

宏任务,进入宏任务队列,在下一轮事件循环执行,例如:

setTimeout(() => {
    task();
},3000)
console.log('执行console');

//执行console
//task()

3.async/await:

async声明异步函数,返回的是promise对象,如果返回的不是promise会自动用Promise.resolve()包装,例如:

async function test(){
    return 'test'
}
test()

返回值为Promise{<resolved>:"test"}

await表示等待右侧表达式的结果,这个结果是promise对象或者其他值。如果是promise对象则等到的结果为Promise状态变为fulfilled之后resolve的参数,如果不是promise对象则直接将该值作为等到的值。

function test(){
    return new Promise(resolve=>{
        setTimeout(()=>resolve('test'),2000);
    });
}

const result=await test();
console.log(result);
console.log('end');

console.log('end')会等到两秒之后执行

由于await会阻塞后面的代码,所以为了避免造成阻塞,await 必须用在 async 函数中,async 函数调用不会造成阻塞。

function test(){
    return new Promise(resolve=>{
        setTimeout(()=>resolve('test'),2000);
    });
}

async function test2(){
    const result=await test();
    console.log(result);
}
test2();
console.log('end');

先执行console.log('end'),两秒后执行console.log('test')

async/await的执行顺序:await是一个让出线程的标志,await后面的函数会先执行一遍,然后就会跳出整个async函数(这里相当于从右向左执行,先执行await后面的东西,碰到await就让出线程阻塞代码),先执行async外面的同步代码,同步代码执行完,再回到async内部,继续执行await后面的代码。

async function test1 () {
    console.log ('start test1') ;
    console.log ( await test2 () ) ;
    console.log ('end test1') ;
}
async function test2 () {
    console.log ('test2') ;
    return await 'return test2 value'
}
test1 () ;
console.log ('start async') ;
setTimeout ( () => {
    console.log ('setTimeout') ;
}, 0) ;
new Promise ( (resolve, reject) => {
    console.log ('promise1') ;
    resolve () ;
}) .then ( () =>{
    console.log ('promise2') ;
 }) ;
console.log ('end async') ;

 上述代码输出:

1.首先执行宏任务,执行test1函数,console.log('start test1');遇到await,执行console.log('test2')

2.执行console.log('start async');

3.遇到setTimeout进入宏任务队列

4.执行new Promise,打印promise1,.then进入微任务队列

5.执行console.log('end async');

6.test1()外面的同步代码执行完毕,回到test1(),执行完console.log(await test2())返回Promise{<resolved>:'return test2 value'},推入微任务队列

7.此时第一个宏任务结束,执行所有的微任务,因为微任务队列先进先出,所以先执行console.log('promise2'),后执行console.log('return test2 value')

8.执行test2完成后,后面的代码不再阻塞,执行console.log('end test1');

9.执行下个宏任务,即执行console.log('setTimeout');

补充:如果去掉test2中return 后面的await,则顺序变为

因为return后面的await阻塞了promise的执行,此时test2()的返回值是一个pending状态的promise,promise只有在resolve或者reject之后才会进入.then中执行回调,因此test2中返回的promise.then在new Promise.then之后进入微任务队列。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值