Promise练习题 I

四处收集的Promise练习题,希望可以更深刻的掌握Promise和JavaScript的执行顺序,来做个汇总,包括题目和自己的理解,如果有错误希望大佬指正。

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

运行结果

上面代码中1,2,4均是同步执行,根据先后顺序输出,promise.then中的代码就是异步执行,需要同步任务执行结束之后才输出。但是要注意这个3的输出也需要resolve()即把Promise的状态更改为成功才会输出。
不更改promise的状态的结果


let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
})
let p2 = p1.then(() => {
  throw new Error('error!!!')
})
console.log('promise1', p1)
console.log('promise2', p2)
setTimeout(() => {
  console.log('promise1', p1)
  console.log('promise2', p2)
}, 2000)
运行结果

这里出现了p2是由p1.then返回的一个全新promise,他的状态是由p1来决定,当一开始输出的时候p1和p2都处于等待状态,1s后p1改变为成功状态,并且p1.then((resolve,reject)),就执行resolve函数,将p2的状态改为拒绝。


let p = new Promise((resolve, reject) => {
  resolve('success1')
  reject('error')
  resolve('success2')
})

p.then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })
运行结果

这里很好理解,Promise的内部代码是同步的,并且状态更改之后不能再次更改。


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

运行结果

Promise.resolve()是将当前的Promise的状态更改为成功,并且传递值,所以下一个then中可以接收到这个值,并且输出。
之后then中的return相当于返回了一个新的Promise,并且携带需要 传递的参数,实现链式调用


let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('once')
    resolve('success')
  }, 1000)
})
let start = Date.now()
p.then((res) => {
  console.log(res, Date.now() - start)
})
p.then((res) => {
  console.log(res, Date.now() - start)
})
运行结果

这个题目也好理解,当1秒之后,promise的状态更改,Promise.then()可以调用多次,同时所有的then都能获取到res的值,并且输出。


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

运行结果

then中就算抛出一个error对象也不会影响Promise的状态,返回任意一个非 promise 的值都会被包裹成 promise 对象,即 return new Error('error!!!') 等价于 return Promise.resolve(new Error('error!!!'))


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

运行结果

这里then内部期望传入函数,如果 不是函数,就发生值穿透,最后console.log是输出的1


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

}));

first().then((arg) => {
    console.log(arg);
});
console.log(4);

第一轮事件循环,先执行宏任务,主script,new Promise立即执行,输出 3,执行p这个new Promise操作,输出 7,发现setTimeout,将回调函数放入下一轮任务队列(Event Quene),p的then,暂且命名为then1,放入微任务队列,且first也有then,命名为then2,放入微任务队列。执行console.log(4),输出 4,宏任务执行结束。

再执行微任务,执行then1,输出 1,执行then2,输出 3.

第一轮事件循环结束,开始执行第二轮。第二轮事件循环先执行宏任务里面的,也就是setTimeout的回调,输出 5.resolve(6)不会生效,因为p的Promise状态一旦改变就不会再变化了。


process.nextTick(() => {
  console.log('nextTick')
})
Promise.resolve()
  .then(() => {
    console.log('then')
  })
setImmediate(() => {
  console.log('setImmediate')
})
console.log('end')

process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。


推荐阅读更多精彩内容