代码输出 —— 异步&事件循环

前言:

        学习笔记,帮助自己理解异步!

正文: 

const promise = new Promise((resolve, reject) => {
  console.log(1);
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);

输出结果:

1

2

4

解释:promise.then 是微任务,它会在所有的宏任务执行完之后才会执行,同时需要promise内部的状态发生变化,,因为这里内部没有发生变化,一直处于pending状态,所以不输出3

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
const promise2 = promise1.then(res => {
  console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);

输出结果:

promise1
1 Promise{<resolved>: resolve1}
2 Promise{<pending>}
resolve1

解释:

1、script是一个宏任务,按照顺序执行这些代码

2、首先进入Promise,执行该构造函数中的代码,打印promise1

3、碰到resolve函数,将promise1的状态改变为resolved,并将结果保存下来

4、碰到peomise1.then这个微任务,将它放入微任务队列

5、promise2是一个新的状态为pending的Promise

6、执行同步代码1,同时打印出promise1的状态是resolved

7、执行同步代码2,同时打印出promise2的状态是pending

8、宏任务执行完毕,查找微任务队列,发现peomise1.then这个微任务且状态为resolved,执行它

const promise = new Promise((resolve, reject) => {
  console.log(1);
  setTimeout(() => {
    console.log("timerStart");
    resolve("success");
    console.log("timerEnd");
  }, 0);
  console.log(2);
});
promise.then((res) => {
  console.log(res);
});
console.log(4);

输出结果:

1

2

4

timerStart

timerEnd

success

解释:

1、首先遇到Promise构造函数,会先执行里面的内容,打印1

2、遇到定时器 setTimeout,它是一个宏任务,放入宏任务队列

3、继续向下执行,打印出2

4、由于Promise的状态此时还是pending,所以promise.then先不执行

5、继续执行下面的同步任务,打印出4

6、此时微任务队列中没有任务,继续执行下一轮宏任务,执行setTimeout

7、首先执行timerStart,然后遇到了resolve,将promise的状态改为resolved且保存结果并将之前的promise.then推入微任务队列,再执行timerEnd

8、执行完这个宏任务,就去执行微任务promise.then,打印出resolve的结果

Promise.resolve().then(() => {
  console.log('promise1');
  const timer2 = setTimeout(() => {
    console.log('timer2')
  }, 0)
});
const timer1 = setTimeout(() => {
  console.log('timer1')
  Promise.resolve().then(() => {
    console.log('promise2')
  })
}, 0)
console.log('start');

输出结果:

start

promise1

timer1

promise2

timer2 

解释:

1、首先Promise.resolve().then是一个微任务,加入微任务队列

2、执行timer1,它是一个宏任务,加入宏任务队列

3、继续执行下面的同步代码,打印出start

4、第一轮宏任务就执行完了,开始执行微任务Promise.resolve().then,打印出promise1

5、遇到timer2,它是一个宏任务,将其加入宏任务队列,此时宏任务队列有两个任务,timer1,timer2

6、这样第一轮微任务就执行完了,开始执行第二轮宏任务,首先执行定时器timer1,打印timer1

7、遇到Promise.resolve().then,它是一个微任务,加入微任务队列

8、开始执行微任务队列中的任务,打印promise2

9、最后执行宏任务timer2定时器,打印出timer2

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

输出结果:

1

解释:

Promise.resolve方法的参数如果是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的Promise对象,状态为resolved,Promise.resolve方法的参数会同时传给回调函数

then方法接受的参数是函数,而如果传递的并非是一个函数,它实际上会将其解释为then(null),会导致前一个Promise的结果会传递下面

第一个then和第二个then中传入的都不是函数,一个是数字,一个是对象,因此发生了透传,将resolve(1)的值直接传到最后一个then里,直接打印出1

原则:好多then的情况下,只需要记住一个原则:.then或.catch的参数期望是函数,传入非函数则会发生值透传

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)

 输出结果:

promise1 {<pending>}

promise2 {<pending>}

error!!!

promise1 {<fulfilled>:"success"}

promise2 {<rejected>:Error:error!!}

解释:

1、首先执行promise1,遇到定时器,放入宏任务队列中

2、执行到promise1.then,但是此时promise1转态还未改变,所以不执行,继续往下

3、输出"promise1"  状态为pending,接着输出 promise2 状态为pending

4、继续往下遇到定时器,放入宏任务队列中,此时宏任务队列中有两个定时器

5、执行宏任务触发resolve,把之前promise.then放入微任务队列中,这个宏任务执行完毕

6、执行微任务,输出error!!!,微任务执行完毕

7、执行宏任务,输出promise1 {<fulfilled>:"success"},promise2 {<rejected>:Error:error}

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

输出结果:

1

2

解释:

Promise是可以链式调用的,由于每次调用.then或者.catch都会返回一个新的promise,从而实现链式调用,并不像一般任务的链式调用一样return this

之所以依次打印出1和2,是因为resolve(1)之后走的第一个then方法,并没有进catch里,所以第二个then中的res得到的实际上是第一个then的返回值。并且return 2会被包装成resolve(2),被最后的then打印输出2

Promise.resolve().then(() => {
  return new Error('error!!!')
}).then(res => {
  console.log("then: ", res)
}).catch(err => {
  console.log("catch: ", err)
})

输出结果:

“then: ” "Error:error!!!"

解释:

返回任意一个非 promise 的值都会被包裹成 promise 对象,因此这里的 return new Error("error!!!")也被包裹成了 return Promise.resolve(new Error('error!!!')),因此它会被then捕获而不是catch

const promise = Promise.resolve().then(() => {
  return promise;
})
promise.catch(console.err)

 输出结果:

Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

解释:

.then.catch 返回的值不能是 promise 本身,否则会造成死循环。

setTimeout(function () {
  console.log(1);
}, 100);

new Promise(function (resolve) {
  console.log(2);
  resolve();
  console.log(3);
}).then(function () {
  console.log(4);
  new Promise((resove, reject) => {
    console.log(5);
    setTimeout(() =>  {
      console.log(6);
    }, 10);
  })
});
console.log(7);
console.log(8);

输出结果: 2 3 7 8 4 5 6 1

1、首先遇到定时器,将其加入到宏任务队列

2、遇到Promise,首先执行里面的同步代码,打印出2,遇到resolve,将其加入到微任务队列,执行后面同步代码,打印出3

3、继续执行script中的代码,打印出7和8,至此第一轮代码执行完成

4、执行微任务队列中的代码,首先打印出4,如遇到promise,执行其中的同步代码,打印出5,遇到定时器,将其加入到宏任务队列中,此时宏任务队列中有两个定时器

5、执行宏任务队列中的代码,这里需要注意的是第一个定时器的时间为100ms,第二个定时器的时间为10ms,所以先执行第二个定时器,打印出6

6、此时微任务队列为空,继续执行宏任务队列,打印出1

Promise.resolve().then(() => {
    console.log('1');
    throw 'Error';
}).then(() => {
    console.log('2');
}).catch(() => {
    console.log('3');
    throw 'Error';
}).then(() => {
    console.log('4');
}).catch(() => {
    console.log('5');
}).then(() => {
    console.log('6');
});

 输出结果:1        3        5        6

解释:

在这道题目中,需要知道,无论是then还是catch中,只要throw抛出了错误,就会被catch捕获,如果没有throw出错误,就被继续执行后面的then

console.log(1);
setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => {
    console.log(3)
  });
});
new Promise((resolve, reject) => {
  console.log(4)
  resolve(5)
}).then((data) => {
  console.log(data);
})
setTimeout(() => {
  console.log(6);
})
console.log(7);

输出结果:1        4        7        5        2        3        6

解释:

1、首先执行script代码,打印出1

2、遇到第一个定时器,将其加入到宏任务队列

3、遇到promise,执行里面的同步代码,打印出4,遇到resolve,将其加入到微任务队列

4、遇到第二个定时器,将其加入宏任务队列中

5、执行script代码,打印出7,至此第一轮执行完成

6、执行微任务队列中的代码,打印出resolve的结果:5;微任务队列为空

7、执行宏任务中第一个定时器,首先打印出2,然后遇到Promise.resolve().then,将其加入到微任务队列中

8、执行完这个宏任务,就开始执行微任务队列,打印出3;微任务全部执行完毕

9、继续执行宏任务队列中的第二个定时器,打印出6 

console.log('1');

setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

输出结果:1、7、6、8、2、4、3、5、9、11、10、12

代码解释:

1、执行script代码,打印1

2、遇到第一个定时器,加入宏任务队列中

3、执行nextTick微任务,将其加入微任务队列中

4、遇到Promise,先打印同步代码,输出7,执行resolve加入微任务对立,此时微任务有两个

5、遇到第二个定时器,加入宏任务队列中,script中代码执行完毕

6、开始执行微任务队列中代码,两个微任务分别输出6 和 8,微任务执行完毕,到此第一个事件循环执行结束,接下来开启第二个事件循环

7、开始执行宏任务队列中的第一个定时器,输出2,往下执行nextTick,加入微任务队列中,往下继续执行遇到promise,输出4,执行resolve,加入微任务队列中

8、第一个定时器中同步代码执行完毕,开始执行微任务队列中代码,微任务队列中有两个微任务分别是nextTick和resolve,分别输出3和5,到此第二个事件循环结束

9、开始执行宏任务队列中第二个定时器,输出9,往下执行nextTick,加入到微任务队列中,往下执行遇到promise,输出11,执行resolve,加入微任务队列中

10、定时器中同步代码执行完毕,开始执行微任务队列中代码,分别输出 10 和 12

Promise.resolve('1')
  .then(res => {
    console.log(res)
  })
  .finally(() => {
    console.log('finally')
  })
Promise.resolve('2')
  .finally(() => {
    console.log('finally2')
  	return '我是finally2返回的值'
  })
  .then(res => {
    console.log('finally2后面的then函数', res)
  })

输出结果:1、finally2、finally、finally2后面的then函数 2

注:

.finally一般用的很少,只需要记住以下几点:

        .finally方法不管Promise对象最后的状态如何都会执行

        .finally方法的回调函数不接受任何的参数,也就是说在.finally函数中是无法知道Promise最终的状态是resolved还是rejected的

        它最终返回的默认会是一个上一次的Promise对象值,不过如果抛出一个异常则返回异常的Promise对象

!!! Promise.resolve是同步执行的,.then()方法中的回调函数是异步执行的,.finally()也是异步执行的。

function runAsync (x) {
    const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
    return p
}

Promise.all([runAsync(1), runAsync(2), runAsync(3)]).then(res => console.log(res))

输出结果

1

2

3

[1,2,3]

解释:

首先,定义了一个Promise来异步执行函数runAsync,该函数传入一个值x,然后间隔一秒后打印出这个x

之后再使用Promise.all来执行这个函数,执行的时候看到一秒之后输出了1,2,3,同时输出了数组[1,2,3],三个函数是同时执行的,并且在一个回调函数中返回了所有的结果。并且结果和函数的执行顺序是一致的

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
function runReject (x) {
  const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
  return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
       .then(res => console.log(res))
       .catch(err => console.log(err))

输出结果:

//1s后输出

1

3

// 2s后输出

2

Error:2

//4s后输出

4

可以看到,catch捕获到了第一个错误,在这道题目中最先的错误是runReject(2)的结果。如果一组异步操作中有一个异常都不会进入.then()的第一个回调函数参数中,会被.then()的第二个回调函数捕获

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log('result: ', res))
  .catch(err => console.log(err))

输出结果:

1

“result”:1

2

3

可以看到,then只会捕获第一个成功的方法,其他的函数虽然还会继续执行,但是不会被then捕获了。

注意:all和race传入的数组中如果有会抛出异常的一步任务,那么只有最先抛出的错误会被捕获,并且是被then的第二个参数或者后面的catch捕获;但是并不会影响数组中其他的异步任务的执行。

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
async1();
console.log('start')

 输出结果:

async1 start

async2

start

async1 end

代码理解:

1、首先执行函数中的同步代码async1 start,之后遇到了await,它会阻塞async1后面代码的执行,因此会先执行async2中的同步代码saync2,单后跳出async1

2、跳出async1函数之后,执行同步代码start

3、在一轮宏任务全部执行完之后,再来执行await后面的内容async end

可以理解为await后面的语句相当于放到了new Promise中,下一行及之后的语句相当于放在了Promise.then中

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
  setTimeout(() => {
    console.log('timer1')
  }, 0)
}
async function async2() {
  setTimeout(() => {
    console.log('timer2')
  }, 0)
  console.log("async2");
}
async1();
setTimeout(() => {
  console.log('timer3')
}, 0)
console.log("start")

 输出结果:

async1 start

async2

start

async1 end

timer2

timer3

timer1

代码解释:

1、首先进入async1,打印async1 start

2、之后遇到async2,进入async2,遇到定时器timer2,加入宏任务队列,之后打印async2

3、由于async2阻塞了后面代码的执行,所以执行后面的定时器timer3,将其加入宏任务队列,之后打印start

4、然后执行async2后面的代码,打印出async1 end,遇到定时器timer1,将其加入宏任务队列

5、最后,宏任务队列有三个任务,先后顺序为timer1,timer3,timer,没有微任务,所以直接所有的宏任务按照先进先出的原则执行

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

代码输出

script start

async1 start

promise1

script end

代码解释:

在async1中await后面的Promise是没有返回值的,也就是它的状态始终是pending状态,所以在await之后的内容是不会执行的,包括async1后面的.then

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
    resolve('promise1 resolve')
  })
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

输出结果 

script start

async1 start

promise1

script end

async1 success

async1 end

代码解释:

1、先输出script start

2、执行async1(),输出async1 start,执行Promise里面同步代码 输出promise1,然后执行resolve,await下一行及以后的代码被阻塞

3、继续执行script中同步代码,打印script end

4、宏任务执行完毕,继续执行被await阻塞的代码,输出async1 success

5、最后执行async1().then,输出async1 end

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}

async function async2() {
  console.log("async2");
}

console.log("script start");

setTimeout(function() {
  console.log("setTimeout");
}, 0);

async1();

new Promise(resolve => {
  console.log("promise1");
  resolve();
}).then(function() {
  console.log("promise2");
});
console.log('script end')

输出结果

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

代码解释

1、开头定义了async1和async2两个函数,但是并未执行,执行script中的代码,所以打印出script start;

2、遇到定时器setTimeout,它是一个宏任务,将其加入到宏任务队列

3、之后执行函数async1,首先打印出async1 start

4、遇到await,执行async2,打印出async2,并阻断后面代码的执行,将后面的代码加入到微任务队列

5、然后跳出async1和async2,遇到Promise,打印出promise1

6、遇到resolve,将其加入到微任务对了,然后执行后面的script代码,打印出script end

7、之后就该执行微任务队列了,首先打印出async1 end,然后打印出promise2

8、执行完微任务队列,就开始执行宏任务队列中的定时器,打印出setTimeout

async function async1 () {
  await async2();
  console.log('async1');
  return 'async1 success'
}
async function async2 () {
  return new Promise((resolve, reject) => {
    console.log('async2')
    reject('error')
  })
}
async1().then(res => console.log(res))

 输出结果:

async2
Uncaught (in promise) error

可以看出,如果async函数中抛出了错误,就会中止错误结果,不会继续向下执行

async function async1 () {
  await Promise.reject('error!!!').catch(e => console.log(e))
  console.log('async1');
  return Promise.resolve('async1 success')
}
async1().then(res => console.log(res))
console.log('script start')

 输出结果

script start
error!!!
async1
async1 success

如果想要让错误不足之处后面的代码执行,可以使用catch来捕获

const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve(6);
            console.log(p)
        }, 0)
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });
}));
first().then((arg) => {
    console.log(arg);
});
console.log(4);

输出结果3、7、4、2、1、5、Promise{<resolved>:1}

代码解释:

1、首先会进入Promise,打印出3,之后进入下面的Promise,打印出7

2、遇到了定时器,将其加入宏任务队列

3、执行Promise p中的resolve,状态变为resolved,返回值为1

4、执行Promise first中的resolve,状态变为resolved,返回值为2

5、遇到p.then,将其加入微任务,遇到first().then,将其加入微任务队列

6、执行外面的代码,打印出4

7、这样第一轮宏任务就执行完了,开始执行微任务队列中的任务,先后打印出1和2

8、这样微任务就执行完了,开始执行下一轮宏任务,宏任务队列中有一个定时器,执行它,打印出5,由于执行已经变为resolved状态,所以resolve(6)不会再执行

9、最后console.log(p)打印出Promise{<resolved>:1}

const async1 = async () => {
  console.log('async1');
  setTimeout(() => {
    console.log('timer1')
  }, 2000)
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 end')
  return 'async1 success'
} 
console.log('script start');
async1().then(res => console.log(res));
console.log('script end');
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .catch(4)
  .then(res => console.log(res))
setTimeout(() => {
  console.log('timer2')
}, 1000)

输出结果:

script start
async1
promise1
script end
1
timer2
timer1

代码解释:

1、首先执行同步带吗,打印出script start;

2 、遇到定时器timer1将其加入宏任务队列;

3 之后是执行Promise,打印出promise1,由于Promise没有返回值,所以后面的代码不会执行;

4 、然后执行同步代码,打印出script end;

5、 继续执行下面的Promise,.then和.catch期望参数是一个函数,这里传入的是一个数字,因此就会发生值渗透,将resolve(1)的值传到最后一个then,直接打印出1;

6、 遇到第二个定时器,将其加入到微任务队列,执行微任务队列,按顺序依次执行两个定时器,但是由于定时器时间的原因,会在两秒后先打印出timer2,在四秒后打印出timer1。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值