promise.then的9道练习题-我猜你不能全对

  1. 摘要
    本文列出了 Promise.then 的各种执行情况的代码,设计了9道题,通过完成这些题目,你会更加精准的掌握Promise的用法。

你可先做试一试,然后拉到最后边检查答案是否正确。欢迎你在最下方留言出你的答题情况。

如果你全部对了(目前我还没有遇到过),请你一定告知我,让我膜拜一下。
如果你觉得有点难,请你认真看完这篇文章中间那一段,如果还是觉得对promise不熟悉,可以先看看这里。

好吧,先来看看题吧

  1. 练习
    第1题
var log= console.log
var p1 = new Promise((res,rej)=>{
    log(1)
    res()
})
log(2)
p1.then(()=>{log(3)})
.then(()=>{log(4)})
.then(()=>{log(5)})  

第2题

var log= console.log
var p1 = new Promise((res,rej)=>{
    log(1)
    res()
})
log(2)
p1.then(()=>{log(3)}).then(()=>{log(4)})
p1.then(()=>{log(5)})

第3题

var log= console.log
var p1 = new Promise((res,rej)=>{
    log(1)
    res()
})
log(2)
p1.then(()=>{log(3)})
log(4)
p1.then(()=>{log(5)})

第4题

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

第5题

new Promise((res, rej) => {
  log(1);
  res();
})
  .then(() => {
    log(2);
    return new Promise((res, rej)=>{
      log(3);
      res();
    })
    .then(()=>{log(4)})
    .then(()=>{log(5)})
  })
  .then(()=>{log(6)})

第6题

new Promise((res, rej)=>{
  log(1);
  res();
})
  .then(() => {
    log(2);
    new Promise((res, rej)=>{
      log(3);
      res();
    })
      .then(() => {log(4);})
      .then(() => {log(5)})
  })
  .then(() => {
    log(6)
  })

第7题

new Promise((res, rej)=>{
  log(1);
  res()
})
  .then(()=>{
    log(2)
    let p = new Promise((res, rej)=>{
      log(3)
      res()
    })
    p.then(()=>{log(4)})
    p.then(()=>{log(5)})
  })
  .then(()=>{log(6)})

第8题

let p = new Promise((res, rej) => {
  log(1)
  res()
})
p.then(() => {
    log(2)
    new Promise((res, rej) => {
      log(3)
      res()
    })
      .then(() => {log(4)})
      .then(() => {log(5)})
})
p.then(() => {log(6)})

第9题 终极考题

let log = console.log
new Promise((res, rej) => {
  log(1)
  res()
})
  .then(() => {
    log(2);
    new Promise((res, rej)=> {
      log(3)
      res()
    })
      .then(()=>{log(4)})
      .then(()=>{log(5)})
    return new Promise((res,rej)=>{
        log(6)
        res()
    })
      .then(()=>{log(7)})
      .then(()=>{log(8)})
  })
  .then(()=>{log(9)})
  1. 如何理解Promise是异步
    new Promise是同步的

定义promise的一般格式:

var p1 = new Promise((res,rej)=>{
    // 代码...这里的代码是同步
  res(1) // res(1)也是同步的
})

示例:
const log = console.log
log(1)
var p1 = new Promise((res,rej)=>{
    log(2)
  res()
})
log(3)
// 输出:1 2 3

上面的代码中,new 构造器中的代码全是同步执行的。

哪到底哪里是异步的呢?
.then中的函数是异步执行的

const log = console.log
console.log(1)
var p1 = new Promise((res,rej)=>{
    log(2)
  res()
})
p1.then(()=>{log(3)})
log(4)
// 输出:1 2 4 3

上面的代码中,当new 语句结束时,p1的状态已经从pending扭转到了resolved,所以p1.then()中的函数会被执行,但是,它却在log(4) 之后才执行。所以我们说它是异步的。

现在,我们搞清楚了一件事:.then中的函数是异步执行的,而不能笼统地说Promise是异步的。

.then会让回调进入微任务队列

还是看一段代码

const log = console.log
console.log(1)
var p1 = new Promise((res,rej)=>{
    log(2)
  res()
})
p1.then(()=>{log(3)}).then(()=>{log(4)})
p1.then(()=>{log(5)})

上面的结果是:1 2 3 4 5 吗?不对!是1 2 3 5 4 . 为啥?
这里先给结论:
.then() 会向 微任务队列做入队操作
整段代码看做宏任务。宏任务结束之后依次执行微任务。

第一步:全部同步任务执行完成(整段代码看做宏任务)。输出 1, 2。p1的状态为resolved,所以log(3)进入微任务,log(5) 也进入微任务。而log(4)所在的then目前还轮到它入队列:原因是p1.then(()=>{log(3)}) 的状态未知。
在这里插入图片描述

第二步:依次执行微任务,取出 ()=>{log(3)} 执行,输出 3 。p1.then(()=>{log(3)}) 的返回值 是一个全新的状态为resolved的promise对象,所以log(4)进入队列
在这里插入图片描述

第三步:依次执行微任务。输出5, 4。

本质上,先做同步代码,再执行异步代码(又细分宏任务和微任务)。

  1. 如何理解同步任务,宏任务,微任务

还是看代码:

const log = console.log
log(1)
var p1 = new Promise((res,rej)=>{
    log(2)
  res()
})
log(3)
setTimeout(()=>{log(4)})
p1.then(()=>{log(5)})
log(6)

结果:1 2 3 6 5 4

分析过程:

整段代码

一个宏任务(整体script)先执行,代码执行的过程中:同步代码立即顺序执行;遇到 异步代码(宏任务或者微任务),就跳过它(不立即执行),视情况把它加入微任务队列和宏任务队列。

一个宏任务中代码全部执行完成(同步代码执行完成,异步代码进入了对应的队列),依次执行微任务队列中的代码;如果微任务队列清空了,则再去进入宏任务队列取下一个宏任务。

常见的宏任务和微任务
宏任务:
  script(整体代码)
  setTimeout
  setInterval
  I/O
  UI交互事件
  postMessage
  MessageChannel
  setImmediate(Node.js 环境)
微任务:
Promise.then
Object.observe
MutaionObserver
process.nextTick(Node.js 环境)

好了,我们完成上面9道题的基础知识就准备就绪了,可以试一试来完成它们了。

  1. 答案

这里的答案在chrome浏览器中验证。

第1题

var log= console.log
var p1 = new Promise((res,rej)=>{
    log(1)
    res()
})
p1.then(()=>{log(3)})
.then(()=>{log(4)})
.then(()=>{log(5)})
log(2)
// 1 2 3 4 5

上面的代码中,三个.then的回调是一个执行完成之后,将后续一个入微任务队列的过程。一个接一个。

第2题

var log= console.log
var p1 = new Promise((res,rej)=>{
    log(1)
    res()
})
p1.then(()=>{log(3)}).then(()=>{log(4)})
p1.then(()=>{log(5)})
log(2)
// 1 2 3 5 4

先log(3)入微任务队列,然后log(5)入微任务列,执行log(2); 到此,整体script完成;
找出微任务队列中的第一个:log(3)执行,同时激活log(4)的那个then,让log(4)进入队列。

在这里插入图片描述

第3题

var log= console.log
var p1 = new Promise((res,rej)=>{
    log(1)
    res()
})
log(2)
p1.then(()=>{log(3)})
log(4)
p1.then(()=>{log(5)})
// 1 2 4 3 5

第4题

var log= console.log
var p1 = new Promise((resolve,reject)=>{
    log(1)
    resolve()
})
console.log(2)
p1.then(()=>{log(3)}).then(()=>{log(4)})
console.log(5)
p1.then(()=>{log(6)}).then(()=>{log(7)})
// 1 2 5 3 6 4 75new Promise((res, rej) => {
  log(1)
  res()
})
  .then(()=>{
    log(2)
    return new Promise((res, rej)=>{
      log(3)
      res()
    })
    .then(()=>{log(4)})
    .then(()=>{log(5)})
  })
  .then(() => {log(6)})
// 1 2 3 4 5 6

上面的代码中有一个特点:第一个then的回调中又有一个return Promise对象,把代码抽象一下:

Promise对象1.then(()=>{ 
  return Promise对象2 
})
  .then(val=>{})

第一个then的返回值 (假定叫p3) 是由Promise对象2来决定的:
p3与Promise对象2不是同一对象,但它们的状态和值(promiseState和promiseValue) 是一致的。
所以,上面的代码中 第二个then一定会等到Promise对象2确定下来之后会进入微任务队列。
第6题

new Promise((res, rej) => {
  log(1)
  res()
})
  .then(() => {
    log(2)
    new Promise((res, rej)=>{
      log(3)
      res()
    })
      .then(()=>{log(4)})
      .then(()=>{log(5)})
  })
  .then(() => {
    log(6)
  })
// 1 2 3 4 6 5

你认真看下第6题与第5题的区别:少了一个return。是吧,其实我们可以把补充一个return undefined, 具体如下 。

new Promise((res, rej)=>{
  log(1)
  res()
})
  .then(() => {
    log(2)
    new Promise((res, rej) => {
      log(3)
      res()
    })
      .then(()=>{log(4)})
      .then(()=>{log(5)})
    return undefined // 这句是补充的
  })
  .then(() => {
    log(6)
  })

如果你不清楚 then(()=>{ return undefined }) 有什么用处,建议回去参考我前面写的Promise-链式用法 。
第7题

new Promise((res, rej) => {
  log(1);
  res()
})
  .then(()=>{
    log(2)
    let p = new Promise((res, rej)=> {
      log(3)
      res()
    })
    p.then(()=>{log(4)})
    p.then(()=>{log(5)})
  })
  .then(()=>{log(6)})
// 1 2 3 4 5 6

第8题

let p = new Promise((res, rej)=> {
  log(1)
  res()
})
p.then(() => {
    log(2)
    new Promise((res, rej)=>{
      log(3)
      res()
    })
      .then(()=>{log(4)})
      .then(()=>{log(5)})
})
p.then(() => {log(6)})
// 1 2 3 6 4 5

第9题 终极考题

let log = console.log
new Promise((res, rej) => {
  log(1)
  res()
})
  .then(() => {
    log(2);
    new Promise((res, rej) => {
      log(3)
      res()
    })
      .then(() => {log(4)})
      .then(() => {log(5)})
    return new Promise((res, rej)=>{
        log(6)
        res()
    })
      .then(() => {log(7)})
      .then(() => {log(8)})
  })
  .then(() => {log(9)})
// 1 2 3 6 4 7 5 8 9

分析过程:
(1)1 是同步代码,先执行;然后 log(2)所在的部分是第一个微任务;
(2)执行这个微任务中,2 3 6 是同步代码这里省略不讲
(3)后续微任务的执行流程如下图(靠左是先进入的,有中划线的表示执行完成的,新加入的在右边):
在这里插入图片描述

  1. 小结

本文列了9道有意设计的题目,希望你会能快速准确的完成这些题目,并掌握背后的原理。

如果对你有帮助,希望你转发,点赞,关注。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值