天天看点

Promise的面试题

一.基础篇

1.1

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
})
console.log('1', promise1);

           

分析:

  • 从上至下,先遇到new Promise,执行该构造函数中的代码promise1(此代码是同步的)
  • 然后执行同步代码1,此时promise1没有被resolve或者reject,因此状态还是pending

结果:

‘promise1’

‘1’ Promise{}

1.2

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

           

分析:

  • 从上至下,先遇到new Promise,执行其中的同步代码1
  • 再遇到resolve(‘success’), 将promise的状态改为了resolved并且将值保存下来
  • 继续执行同步代码2
  • 跳出promise,往下执行,碰到promise.then这个微任务,将其加入微任务队列
  • 执行同步代码4
  • 本轮宏任务全部执行完毕,检查微任务队列,发现promise.then这个微任务且状态为resolved,执行它。

结果:

1 2 4 3

1.3

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

           

分析:

和题目二相似,只不过在promise中并没有resolve或者reject

因此promise.then并不会执行,它只有在被改变了状态之后才会执行。

结果:

1 2 4

1.4

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);

           

分析:

从上至下,先遇到new Promise,执行该构造函数中的代码promise1

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

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

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

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

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

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

结果:

‘promise1’

‘1’ Promise{: ‘resolve1’}

‘2’ Promise{}

‘resolve1’

二.结合setTimeout

1.1

console.log('start')
setTimeout(() => {
  console.log('time')
})
Promise.resolve().then(() => {
  console.log('resolve')
})
console.log('end')

           

分析:

刚开始整个脚本作为一个宏任务来执行,对于同步代码直接压入执行栈进行执行,因此先打印出start和end。

setTimout作为一个宏任务被放入宏任务队列(下一个)

Promise.then作为一个微任务被放入微任务队列

本次宏任务执行完,检查微任务,发现Promise.then,执行它

接下来进入下一个宏任务,发现setTimeout,执行。

结果:

‘start’

‘end’

‘resolve’

‘time’

1.2

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);

           

分析:

从上至下,先遇到new Promise,执行该构造函数中的代码1

然后碰到了定时器,将这个定时器中的函数放到下一个宏任务的延迟队列中等待执行

执行同步代码2

跳出promise函数,遇到promise.then,但其状态还是为pending,这里理解为先不执行

执行同步代码4

一轮循环过后,进入第二次宏任务,发现延迟队列中有setTimeout定时器,执行它

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

继续执行同步代码timerEnd

宏任务全部执行完毕,查找微任务队列,发现promise.then这个微任务,执行它。

结果:

结果就不给啦,自己去试试吧

1.3

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');

           

分析:

刚开始整个脚本作为第一次宏任务来执行,我们将它标记为宏1,从上至下执行

遇到Promise.resolve().then这个微任务,将then中的内容加入第一次的微任务队列标记为微1

遇到定时器timer1,将它加入下一次宏任务的延迟列表,标记为宏2,等待执行(先不管里面是什么内容)

执行宏1中的同步代码start

第一次宏任务(宏1)执行完毕,检查第一次的微任务队列(微1),发现有一个promise.then这个微任务需要执行

执行打印出微1中同步代码promise1,然后发现定时器timer2,将它加入宏2的后面,标记为宏3

第一次微任务队列(微1)执行完毕,执行第二次宏任务(宏2),首先执行同步代码timer1

然后遇到了promise2这个微任务,将它加入此次循环的微任务队列,标记为微2

宏2中没有同步代码可执行了,查找本次循环的微任务队列(微2),发现了promise2,执行它

第二轮执行完毕,执行宏3,打印出timer2

结果:

‘start’

‘promise1’

‘timer1’

‘promise2’

‘timer2’

promise中的then,catch

1.1

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);
  })

           

分析:

构造函数中的 resolve 或 reject 只有第一次执行有效,多次调用没有任何作用 。

结论:Promise的状态一经改变就不能再改变。

所以答案是什么呢?

1.2

const promise = new Promise((resolve, reject) => {
  reject("error");
  resolve("success2");
});
promise
.then(res => {
    console.log("then1: ", res);
  }).then(res => {
    console.log("then2: ", res);
  }).catch(err => {
    console.log("catch: ", err);
  }).then(res => {
    console.log("then3: ", res);
  })

           

结果:

"catch: " “error”

"then3: " undefined

分析:

至于then3也会被执行,那是因为catch()也会返回一个Promise,且由于这个Promise没有返回值,所以打印出来的是undefined。

1.3

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

           

结果: 1 2

分析:

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

上面的输出结果之所以依次打印出1和2,那是因为resolve(1)之后走的是第一个then方法,并没有走catch里,所以第二个then中的res得到的实际上是第一个then的返回值。

且return 2会被包装成resolve(2)。

async and await

1.1

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

           

注:这里首先要明白async和promise的关系,明白用promise形式是如何书写代码的哦,可以查看async和await的简介

上面代码等同于

// 等同于

    function async1(){
        return new Promise((resolve,reject)=> {
            console.log("async1 start");
            async2().tnen(res => {
                console.log("async1 end");
                resolve()
            })
        })
    }

    function async2(){
        return new Promise((resolve,reject) => {
            console.log("async2");
        })
    }
    async1()
    console.log('start')
           

这下知道结果是什么了吧

1.2

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

           

结果:

‘async1 start’

‘async2’

‘start’

‘async1 end’

‘timer’

1.3

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")

           

哈哈哈,有没有绕晕你呀,自己去解锁吧