目录

1.前言:

2.本篇文章的目的有三个:

3.作用:

4.格式:

5. 链式使用

6.链式调用的核心

7. 项目中的回调地狱和代码优化

8.Promise常见面试题整理

8.1 Promise 有几种状态,什么时候会进入 catch?

8.2  setTimeout、Promise、Async/Await 的区别?

8.3 、Promise 构造函数是同步执行还是异步执行,那么 then 方 法呢?

8.4 Promise 只有成功和失败 2 个状态,怎么让一个函数无论成 功还是失败都能被调用? 

8.5  Promise 中 reject 和 catch 处理上有什么区别

8.6  手写一个 Promise(高薪常问)

8.7 Promise 如何封装一个 Ajax(高薪常问)

8.8 分析下列程序代码,得出运行结果,解释其原因


1.前言:


本文前半部分讲述如何使用,后面讲述promise的核心功能,最后是整理的面试题。


注:文中所有代码都可直接复制在浏览器控制台运行。


2.本篇文章的目的有三个:


第一,会用Promise

第二,能看懂别人写的Promise代码

第三,能够回答面试中大多数的promise相关问题


3.作用:


解决回调地狱问题,另一种异步代码的写法(之前就是回调函数)。

注:若有人问,一定能解决回调地狱问题吗?答案是不一定——必须是链式写法且不能嵌套才可以


 4.格式:


4.1 promise的格式:

有三个状态,和一个值。注意初始状态pending啥也不是,可理解为一个起点,要么指向成功,要么指向失败。

<script>

  var p1 = new Promise((resolve, reject)=>{
    // 异步代码,这个区间的代码是同步的,在then里面的才是异步的,后面会介绍,别误会了。
     resolve({a:1})
    //reject({a:1})
  })

  // promise对象:
  // 1. 状态 pending, resolved(fulfilled) 成功, rejected 失败
  // 2. 值。resolve,reject调用时传入的数据

  console.log(p1)

</script>

4.2 控制台打印如下:

presto常见面试题_Promise

 4.3 小结:

promise对象有三个状态:pending(初始), resolved成功(实测浏览器显示fulfilled,resolved是之前大家伙默认的),rejected 失败

resolve, reject是两个形参,且是函数


5. 链式使用


最核心也是最常见的方法,务必掌握。

当p1的状态从pending --> resolved时会执行then函数

当p1的状态从pending --> rejected时会执行catch函数

then、catch函数里的参数(此处是obj)代表promise里面的值

<script>

  var p1 = new Promise((resolve, reject)=>{
    // 异步代码
    // resolve({a:1})
    setTimeout(() => {
      // resolve({a:1})
      reject('abc')
    },3000)
  })
  // 当p1的状态从pending --> resolved时会执行
  p1.then((obj)=>{
    // obj就是p1的promiseValue
    console.log('then...', obj)
  }).catch(err => { // 当p1的状态从pending --> rejected时会执行
     // err就是p1的promiseValue
    console.log('catch...',err)
  })


</script>

 控制台打印:

presto常见面试题_es6_02


6.链式调用的核心


链式调用时,会返回一个新的promise对象

链式调用时,如果返回值不是promise,则状态一定为成功,值为链式调用的参数。

链式调用时,如果返回值是promise,则状态是promise的状态,值为promise对象的值。

返回值没有,如不写return时,返回值是undefined

<script>

  var p1 = new Promise((resolve, reject)=>{
    resolve({a:1})
  })

  var p2 = p1.then(obj => {
    // return 100 返回值不是promise,所以状态是成功,返回值是100
    let  p = new Promise((res,rej)=>{
      rej(123)
    })
    return p
  })
  // 它返回值是一个新的promise对象
  // p2的proimseState,promiseValue如何决定?
  //    (1) 如果返回值val不是Promise,  则p2的proimseState====成功 , promiseValue===val
  //    (2) 如果返回值val是Promise对象,
  //         则p2的proimseState====Promise对象的状态 , promiseValue===Promise对象的val
  // console.log(p2)

  const p3 = p2.then(res=>{
    console.log(res)  //注意:这里没有return  默认return是underfined
  }).catch(err=>{
    console.log('err',err)// 123
  })

  console.log('p3',p3)


</script>

6.1重要结论:

如果没有返回promise对象的话,可以无限的点then,这个then继承上一个then的状态和值,可以一直链式下去。直到遇到return 一个promise对象才会根据这个对象的状态和值,不再继承之前的


7. 项目中的回调地狱和代码优化


7.1 回调地狱的写法

presto常见面试题_Promise_03

7.2 改成链式调用(代码优化)

presto常见面试题_Promise_04

7.3 项目实际代码:

// 修改sku
    updateCartSku (ctx, { newSku, oldSkuId }) {
      console.log('updateCartSku', newSku)

      return new Promise((resolve, reject) => {
        if (ctx.rootState.user.profile.token) {
          console.log('按登录的去做')
          const oldSku = ctx.state.list.find(it => it.skuId === oldSkuId)
          const count = oldSku.count // 获取数据
          // 1. 删除
          // 2. 添加
          // 3. 获取最新的购物车
          deleteCart([oldSkuId])
            .then(() => insertCart({ skuId: newSku.skuId, count }))
            .then(() => findCartList())
            .then(data => {
              ctx.commit('setCartList', data.result)
              resolve()
            })
        } else { // 没有登录
          ctx.commit('updateCartSku', { newSku, oldSkuId })
          resolve()
        }
      })
    },

 7.4 小结:

用Promise确实可以解决回调地狱的问题,但要注意,要链式写,而不要嵌套,不然亦然陷入回调地狱的问题


8.Promise常见面试题整理


8.1 Promise 有几种状态,什么时候会进入 catch?

Promise 有几种状态

三个状态:pending、fulfilled、reject

两个过程:padding -> fulfilled、padding -> rejected Promise

什么时候会进入 catch 当 pending 为 rejectd 时,会进入 catch


8.2  setTimeout、Promise、Async/Await 的区别?

事件循环中分为宏任务队列和微任务队列

宏任务(macrotask):在新标准中叫 task 主要包括:script(整体代码),setTimeout,setInterval,setImmediate,I/O,ui

微任务(microtask):在新标准中叫 jobs 主要包括:process.nextTick,Promise,MutationObserver(html5 新特性)

setTimeout、Promise、Async/Await 的区别

setTimeout 的回调函数放到宏任务队列里,等到执行栈清空以后执行

Promise.then 里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执 行完再执行

async 函数表示函数里面可能会有异步方法,await 后面跟一个表达式

async 方法执行时,遇到 await 会立即执行表达式,然后把表达式后面的代码放到微任务队 列里,让出执行栈让同步代码先执行


8.3 、Promise 构造函数是同步执行还是异步执行,那么 then 方 法呢?

 Promise 构造函数是同步执行的,then 方法是异步执行


8.4 Promise 只有成功和失败 2 个状态,怎么让一个函数无论成 功还是失败都能被调用? 

使用 Promise.all()

Promise.all()用于将多个 Promise 实例,包装成一个新的 Promise 实例

Promise.all()接受一个数组作为参数,数组里的元素都是 Promise 对象的实例,如果不是, 就会先调用下面讲到的 Promise.resolve(),将参数转为 Promise 实例,再进一步处理。

(Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都 是 Promise 实例。


8.5  Promise 中 reject 和 catch 处理上有什么区别

reject 是用来抛出异常,catch 是用来处理异常

reject 是 Promise 的方法,而 catch 是 Promise 实例的方法

reject 后的东西,一定会进入 then 中的第二个回调,如果 then 中没有写第二个回调,则进入 catch

网络异常(比如断网),会直接进入 catch 而不会进入 then的第二个回调


8.6  手写一个 Promise(高薪常问)

var Promise = new Promise((resolve, reject) => {
if (操作成功) {
resolve(value)
} else {
reject(error)
}
})
Promise.then(function (value) {
// success
}, function (value) {
// failure
})

 8.7 Promise 如何封装一个 Ajax(高薪常问)

presto常见面试题_前端_05

presto常见面试题_前端_06


8.8 分析下列程序代码,得出运行结果,解释其原因

<script>

var p1 = new Promise((resolve, reject) => {
resolve('success1')
reject('error')
resolve('success2')
})
p1.then((res) => {
console.log('then: ', res)
}).catch((err) => {
console.log('catch: ', err)
})
console.log(p1);  // then: success1
</script>

解析:构造函数中的 resolve 或 reject 只有第一次执行有效,多次调用没有任何作用,呼 应代码二结论:Promise 状态一旦改变则不能再变