Promise学习

如果你还没有了解同步异步是什么,请先学习上一篇文章
https://blog.nowcoder.net/n/dd156579f7b8463b992e264ba7ab72da

Promise是为了解决什么问题:

1、主要用于异步计算
2、可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
3、可以在对象之间传递和操作promise,帮助我们处理队列

以前我们解决异步回调是通过函数的嵌套调用,这样剥夺了函数return的能力,如果嵌套层次多了,比如为了完成一个功能,我们需要调用api1、api2、api3,依次按顺序调用,这个时候就会存在回调地狱的问题,使得代码非常之不优雅,可读性很差,维护困难,promise就是为了解决回调地狱,让代码看起来更加优雅、简洁。

Promise的特点:

  • Promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以(闭包除外)
  • Promise没有剥夺函数return的能力,因此无需层层传递callback进行回调获取数据
  • Promise代码风格容易理解,便于维护
  • Promise方便解决多个异步等待合并
  • Promise一共有三个状态:准备状态(pending)、解决状态(fulfilled)、拒绝状态(rejected)当promise状态发生改变,会触发then()方法里面的响应函数处理后续步骤,promise状态一经改变,就不会再变了(状态凝固)。
  • Promise会产生微任务

Promise微任务处理机制:

例:比方说我今天要去吃肯德基,一种方式是有很多人排队,我需要等我的餐做好之后再拿餐走人,如果做的时间需要十分钟,我就需要站着排队等十分钟后才能走,这种情况就需要等待一件事情做完以后才能继续做下一件事情,非常不方便;另一种方式是我点餐(promise)以后可以拿号去找个桌子坐下,等到餐做好了叫号通知我已经做完了(resolve),可能这个时候我正在打电话,等我打完电话再过去拿餐(等待同步任务执行完毕再处理任务队列),或者服务员说没食材了这个餐做不了(reject),我可以选择走人去隔壁吃个必胜客啥的,显然这种方式更加省事,不用站在那一直傻等着。

resolve和reject都会创建一个微任务,微任务可以创建多个,resolve创建的微任务使用.then接收,reject创建的微任务使用.catch接收

promise队列原理:

队列中的每一个成员都要是一个promise,下一个成员必须等到上一个成员状态发生改变才会执行。

举例:使用map(别的循环方法也可以)实现promise队列:

function fn(num) {
  let promise = Promise.resolve();

  num.map(v => {
    promise = promise.then(_ => {
      return new Promise((resolve => {
        setTimeout(() => {
          console.log(v);
          resolve(); // 上一个成员状态改变后下一个成员才会执行
        }, 1000)
      }))
    })
  })
}

fn([1,2,3,4,5]);

参考文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

宏任务和微任务的执行顺序:

promise执行会生成微任务,setInterval | setTimeout生成宏任务,任务队列会先执行微任务,后执行宏任务,比方家里有大人有小孩,大人饿了可以先忍一忍,要先把小孩的奶喂了,这是一种执行机制,宏任务和微任务都属于异步,需要等同步代码执行完毕后才会运行,类似于你去银行办业务让vip客户先办,你得等vip客户办完之后才能办,此时,同步代码就是vip客户。

// 宏任务
setTimeout(() => {
  console.log('setTimeout')
}, 0)

// 微任务
new Promise((resolve, reject) => {
  resolve(1);
  console.log('promise'); // 同步代码
}).then((value) => { 
  console.log(value)
})

console.log('同步'); // 同步代码

输出结果:

图片说明

思考题:输出的先后顺序是什么?

// 微任务
new Promise((resolve, reject) => {
  // 宏任务
  setTimeout(() => {
    console.log('setTimeout')
    resolve(1);
  }, 0)
  console.log('promise'); // 同步代码
}).then((value) => { 
  console.log(value)
})

console.log('同步'); // 同步代码

解题:① promise ② 同步 ③ setTimeout ④ 1

先执行同步任务输出‘promise’跟‘同步’,由于微任务的创建是在宏任务中执行的,此时宏任务执行前微任务还没有被创建,因此会先把宏任务拿到主线程执行输出'setTimeout',然后再把resolve创建的微任务存入任务队列中,等到当前宏任务(setTimeout)执行完毕后再将微任务拿到主线程当中执行。

可以验证,下面代码的运行结果跟上面是一样的

// 微任务
new Promise((resolve, reject) => {
  // 宏任务
  setTimeout(() => {
    resolve(1);
    console.log('setTimeout')
  }, 0)
  console.log('promise'); // 同步代码
}).then((value) => { 
  console.log(value)
})

console.log('同步'); // 同步代码

Promise单一状态与状态中转:

只有当改变状态的时候才会产生微任务,如果状态改变的时间比较晚,微任务产生的时间也会比较晚。

new Promise((resolve, reject) => {
  resolve('成功'); // 改变状态产生微任务
}).then(msg => { // 处理微任务
  console.log(msg);
})

console.log('同步');

------------------------------------------------------------------

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功'); // 延迟两秒后才改变状态产生微任务
  }, 2000)
}).then(msg => { // 会等到微任务产生后才处理微任务
  console.log(msg); // 两秒后输出'成功'
})

console.log('同步');

------------------------------------------------------------------

let p1 = new Promise((resolve, reject) => {
  resolve('成功'); // 改变状态产生微任务
})
new Promise((resolve, reject) => {
  resolve(p1); // 将另一个promise状态传递
}).then(msg => { // 处理微任务
  console.log(msg); // 输出'成功'
})

console.log('同步');

------------------------------------------------------------------

let p1 = new Promise((resolve, reject) => {
  resolve('成功'); // 改变状态产生微任务
})
new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(p1); // 延迟两秒后将另一个promise状态传递
  }, 2000)
}).then(msg => { // 处理微任务
  console.log(msg); // 延迟两秒后输出'成功'
})

console.log('同步');

promise状态改变后微任务就已经产生了,是不可逆的,也就是说不可以再撤销,比如你去点奶茶,奶茶做好了就不能撤销了,喝就完事了。

let p1 = new Promise((resolve, reject) => {
  reject('失败'); // 产生失败微任务
})
new Promise((resolve, reject) => {
  // 状态已经在p1产生了,在当前promise中使用resolve也不可以重新改变状态
  resolve(p1);
}).then(msg => { // 处理微任务
  console.log(msg);
}, err => {
  console.log('err' + err); // 走失败微任务处理程序,输出'err失败'
})

console.log('同步');

Promise.then也是一个promise:

let p1 = new Promise((resolve, reject) => {
  resolve('success');
})

// .then执行完后会返回promise对象
let p2 = p1.then(msg => {}, err => {});

console.log(p1);
console.log(p2);

输出结果:

图片说明

因此.then后面的链式.then都是对上一个返回的promise的处理

let p1 = new Promise((resolve, reject) => {
  resolve('success');
})

let p2 = p1
  .then(
    msg => console.log(msg), // success
    err => console.log(err)
  )
  .then(
    a => console.log('成功'), // 成功
    b => console.log(b)
  )

输出结果:

图片说明

新生成的promise( p1.then() )默认返回的状态就是成功,即使p1中的状态改成reject,p1.then().then()还是会执行成功任务

let p1 = new Promise((resolve, reject) => {
  reject('fail');
})

let p2 = p1
  .then(
    msg => console.log(msg), 
    err => console.log(err)  // fail
  )
  .then(
    a => console.log('成功'), // 成功
    b => console.log(b)
  )

输出结果:

图片说明

通过给上一个.then返回(return)新的promise,可以改变下一个.then对它的处理方式

let p1 = new Promise((resolve, reject) => {
  resolve('success');
})

let p2 = p1
  .then(
    msg => {
      return new Promise((resolve, reject) => {
        reject('处理失败')
      })
    },
    err => console.log(err)
  )
  .then(
    a => console.log('success ' + a),
    // 此时处理的状态是上一个.then中return的promise的状态
    b => console.log('fail ' + b) // fail 处理失败
  )

输出结果:

图片说明

promise其他类型的写法:如果一个对象或者类中包含then方法,js会自动包装成promise

let p1 = new Promise((resolve, reject) => {
  resolve('success');
})

let p2 = p1
  .then(
    msg => {
      // 1、直接返回一个对象,只要其中有then方法,就会被解析为promise
      return {
        then(resolve, reject) {
          resolve('成功');
        }
      }
    },
    err => console.log(err)
  )
  .then(
    a => console.log(a), // 成功
    b => console.log(b)
  )

  -------------------------------------------------------------------

let p1 = new Promise((resolve, reject) => {
  resolve('success');
})

let p2 = p1
  .then(
    msg => {
      // 2、返回构造函数实例
      class R {
        then(resolve, reject) {
          resolve('成功');
        }
      }

      return new R();
    },
    err => console.log(err)
  )
  .then(
    a => console.log(a), // 成功
    b => console.log(b)
  )

  -------------------------------------------------------------------

let p1 = new Promise((resolve, reject) => {
  resolve('success');
})

let p2 = p1
  .then(
    msg => {
      // 3、返回静态方法
      return class  {
        static then(resolve, reject) {
          resolve('我是静态方法')
        }
      }
    },
    err => console.log(err)
  )
  .then(
    a => console.log(a), // 我是静态方法
    b => console.log(b)
  )

Promise.catch用法:

catch可以统一处理前面所有的then抛出的错误状态或者是其他错误,推荐放在最后调用,如果前面的.then中写了处理reject的回调,优先执行回调。使用catch就不用每个.then中都处理reject了

let p1 = new Promise((resolve, reject) => {
  resolve('success');
})

let p2 = p1
  .then(
    msg => {
      return new Promise((resolve, reject) => {
        reject('reject');
      })
    })
  .then(
    a => console.log(a),
  )
  .catch(
    err => console.log(err) // reject
  )
全部评论

相关推荐

11-09 01:22
已编辑
东南大学 Java
高级特工穿山甲:羡慕,我秋招有家企业在茶馆组织线下面试,约我过去“喝茶详谈”😢结果我去了发现原来是人家喝茶我看着
点赞 评论 收藏
分享
10-30 23:23
已编辑
中山大学 Web前端
去B座二楼砸水泥地:这无论是个人素质还是专业素质都👇拉满了吧
点赞 评论 收藏
分享
1 1 评论
分享
牛客网
牛客企业服务