[js] promise 与 event loop
1
Promise 构造函数与 console.log() 在stack里,按顺序执行。
promise.then 是microtask,在queue里,stack执行完后才会执行它。
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
//1
//2
//4
//3
2
Promise 构造函数与 console.log() 在stack里,按顺序执行。
promise.then 是 microtask,在 queue 里,stack执行完后才会执行它。
setTimeout 是 macrotask 也在 queue 里,microtask执行完后才会执行它。
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('promise11', promise1)
console.log('promise22', promise2)
}, 2000)
//promise1 Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
//promise2 Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
//Uncaught (in promise) Error: error!!!
//promise11 Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "success"}
//promise22 Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: error!!!
promise 有 3 种状态:pending、fulfilled 或 rejected。状态改变只能是 pending->resolved 或者 pending->rejected,状态一旦改变则不能再变。
每次then之后都返回的是一个基于前一个promise对象的新promise对象。
上面 promise2 并不是 promise1,而是返回的一个新的 Promise 实例。
3
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)
})
//then: success1
构造函数中的 resolve 或 reject 只有第一次执行有效,多次调用没有任何作用,promise 状态一旦改变则不能再变。
4
Promise.resolve(1)
.then((res) => {
console.log(res)
return 2
})
.catch((err) => {
return 3
})
.then((res) => {
console.log(res)
})
//1
//2
promise 可以链式调用。提起链式调用我们通常会想到通过 return this 实现,不过 Promise 并不是这样实现的。promise 每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用。
5
const start = Date.now()
在 和 promise构造函数在stack里;
虽然promise.then是microtask,setTimeout是macrotask,但是promise.then只有resolve或reject了才会触发(才会进入queue)。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('once')
resolve('success')
}, 1000)
})
const start = Date.now();
promise.then((res) => {
console.log(res, Date.now() - start)
})
promise.then((res) => {
console.log(res, Date.now() - start)
})
//once
//success 1001
//success 1002
promise 的 .then 或者 .catch 可以被调用多次,但这里 Promise 构造函数只执行一次。或者说 promise 内部状态一经改变,并且有了一个值,那么后续每次调用 .then 或者 .catch 都会直接拿到该值。
6
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
//then: Error: error!!!
.then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获,因为返回任意一个非 promise 的值都会被包裹成 promise 对象,即
return new Error('error!!!')
等价于
return Promise.resolve(new Error('error!!!'))
。
需要改成其中一种:
return Promise.reject(new Error('error!!!'))
throw new Error('error!!!')
7
const promise = Promise.resolve()
.then(() => {
return promise
})
promise.catch(console.error)
//TypeError: Chaining cycle detected for promise #<Promise>
.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。类似于:
process.nextTick(function tick () {
console.log('tick')
process.nextTick(tick)
})
8
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
//1
.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。
9
Promise.resolve()
.then(function success(res) {
throw new Error('error')
}, function fail1(e) {
console.error('fail1: ', e)
})
.catch(function fail2(e) {
console.error('fail2: ', e)
})
//fail2: Error: error
.then 可以接收两个参数,第一个是处理成功的函数,第二个是处理错误的函数。.catch 是 .then 第二个参数的简便写法,但是它们用法上有一点需要注意:.then 的第二个处理错误的函数捕获不了第一个处理成功的函数抛出的错误,而后续的 .catch 可以捕获之前的错误。
当然以下代码也可以:
Promise.resolve()
.then(function success1(res) {
throw new Error('error')
}, function fail1(e) {
console.error('fail1: ', e)
})
.then(function success2(res) {
console.log('success2: ', res)
}, function fail2(e) {
console.error('fail2: ', e)
})
//fail2: Error: error
10
console.log 属于 stack。
process.nextTick 和 promise.then 属于 microtask,
setImmediate 属于 macrotask。
process.nextTick(() => {
console.log('nextTick')
})
Promise.resolve()
.then(() => {
console.log('then')
})
setImmediate(() => {
console.log('setImmediate')
})
console.log('end')
//end
//nextTick
//then
//setImmediate
11
new Promise(resolve => {
resolve(new Promise(reject => {
setTimeout(() => {
reject(Promise.resolve(1))
}, 3000)
}))
})
.then(console.log)
.catch(console.error)
//1
resolve一个promise实例时,这个resolve的promise会继续执行下去,直到resolve的不是promise,then才会执行.
12
const promise1 = new Promise((resolve, reject) => {
resolve('success')
})
const promise2 = new Promise((resolve, reject) => {
resolve('success')
})
const promise3 = new Promise((resolve, reject) => {
resolve('success')
})
console.log('start')
setTimeout(() => {
promise2.then(() => {
console.log('a2')
setTimeout(() => {
console.log('a3')
}, 1000)
})
promise3.then(() => {
console.log('a4')
})
console.log('a1')
}, 1000)
promise1.then(() => {
console.log('b1')
setTimeout(() => {
console.log('b2')
}, 1000)
})
setTimeout(() => {
console.log('c1')
}, 1000)
console.log('end')
/*
start
end
b1
a1
a2
a4
c1
b2
a3
*/
3个概念:
stack
任务执行栈,所有的同步任务都算作stack,同步任务优先级最高.
一次完整的stack执行也有两种情况:
- 一种是全部执行完同步任务后去执行microtask队列里的所有microtask.
- 一种是任务栈是空的,从macrotask队列找寻能执行的一个macrotask,然后再去执行microtask队列里的所有microtask.
一次完整的stack执行后会执行 UI rendering,但是不一定就会渲染.
macrotask
如onclick,setTimeout,ajax请求等都算作macrotask,回调了进入macrotask队列(事件队列).
microtask
如promise.
promise也是在回调后才会进入microtask队列.
一个stack执行完后会执行所有microtask队列里的microtask.