前言:
学习笔记,帮助自己理解异步!
正文:
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。