【面试高频知识点】promise的理解(通俗易懂)

前一段时间找工做,面试了大大小小十几家公司,其中也包含了腾讯、blue等知名公司。总结面试经历,发现本身还有不少不足的地方,许多知识点都知其然不知其因此然。趁着最近事比较少,会陆陆续续总结一些面试的高频知识点,提高本身知识的深度和广度。本文是系列文章之一:promise的理解。javascript

1、何为promise,咱们为什么要使用它?

因为JavaScript语言特性,全部程序都是单线程执行的。因为这个特性,JavaScript的一些浏览器事件、请求事件都是异步执行的,经过回调函数处理异步的结果。这是很常见的语法,可是在一些场景下,就会造成回调函数嵌套回调函数,有的状况甚至套用多层,造成了“回调地狱”,这样使得代码臃肿可读性差并且难以维护。php

<!--一个地狱回调的例子,上一个函数的回调结果被下一个函数所依赖-->
const verifyUser = function(username, password, callback){
   require.verifyUser(username, password, (error, userInfo) => {
       if (error) {
           callback(error)
       }else{
           require.getRoles(userInfo, (error, roles) => {
               if (error){
                   callback(error)
               }else {
                   require.logAccess(roles, (error) => {
                       if (error){
                           callback(error);
                       }else{
                           callback(null, userInfo, roles);
                       }
                   })
               }
           })
       }
   })
}
复制代码

为了解决这种问题,社区提出了一些解决方案,采用链式调用的方法,来解决异步回调,并在在ES6被统一成规范。能够说Promise 是异步编程的一种解决方案。前端

2、Promise的基本用法

基本用法

做为新的规范,promise采用更加直观也更加易读的方式解决回调嵌套。ES6规定,promise对象是一个构造函数,经过new关键字来生成实例。下面是promise的基本用法java

<!--promise的基本用法-->
 const promise = new Promise((resolve, reject) => {
  // 异步操做的代码
  if (success){
    resolve(value);
  } else {
    reject(error);
  }
});
复制代码

Promise构造函数接受一个函数做为参数,该函数的两个参数分别是resolve和reject,他们是JavaScript引擎提供的两个函数。异步操做有两种结果:成功或失败 ios

promise内部状态转变

  • resolve函数在异步操做由pending状态(执行进行中)变为resolved(成功状态)时触发,传递操做成功后的结果;
  • reject函数在异步操做从pending状态(执行进行中)变为rejected(失败状态)时触发,传递操做失败后的结果。

注意promsie状态 只能由 pending => fulfilled/rejected, 一旦修改就不能再变程序员

promise对象方法

Promise.prototype.then()

那么问题来了,刚才咱们说到promise状态改变后出触发相应的函数,那么咱们处理状态改变的代码要写在哪里呢? 没错,就是then()方法。then方法是定义在原型对象Promise.prototype上的,它的做用是为 Promise 实例添加状态改变时的回调函数then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。面试

<!--promise then方法-->
 const promise = new Promise((resolve, reject) => {
   resolve('fulfilled'); // 状态由 pending => fulfilled
 }).then(result => { 
    console.log(result); // 'fulfilled' 
 }, reason => { 
    
 })
复制代码
const promise = new Promise((resolve, reject) => {
  reject('rejected '); // 状态由 pending => rejected
}).then(result => { 
    
}, reason => { 
   console.log(reason); // 'rejected'
})
复制代码

上边说过,promise状态一旦修改就不能再变 只能由 pending => fulfilled或者 pending => rejected编程

promise采用链式调用,then()为 Promise 注册回调函数,参数为上一个任务的返回结果,因此链式调用里then 中的函数必定要 return 一个结果或者一个新的 Promise 对象,才可让以后的then 回调接收。axios

const promise = new Promise((resolve, reject) => {
     resolve("success")
 })
.then(result => {
    return result
})
.then(result => {
    console.log(result)  // "success"
})
.catch(err => {})
复制代码

Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,也就是异步操做发生错误时的回调函数,另外,then()方法里的回调函数发生错误也会被catch()捕获。api

<!--promise catch方法-->
 const promise = new Promise((resolve, reject) => {
   throw new Error('err');
   // 或者reject(new Error('err'));
 }).then(result => { 
    console.log(result); 
 }).catch(err => {
    // 处理前两个promise产生的错误
     console.log(err)
 })
复制代码

到这里,细心的同窗会发现,既然我then()方法第二个参数能够用来抛出错误,干吗还要用这个catch()方法。 其实仍是有区别的,在链式操做里,任何promise抛出的同步或异步错误均可以被then()方法捕获,而reject则处理当前promise的错误。所以,建议不要在then方法里面定义 reject 状态的回调函数(即then的第二个参数),老是使用catch方法,这样也更接近同步的写法(try/catch)。

<!--promise catch方法-->
const promise = new Promise((resolve, reject) => { 
    // some code
})
// good
promise.then(result => { 
   // success
}).catch(err => {
    // err
})
// not recommend
promise.then(result => {
    //success
},err => {
    //err
});
复制代码

Promise.prototype.finally()

finally()方法是在ES2018引入标准的,该方法表示promise不管什么状态,在执行完then()或者catch()方法后,最后都会执行finally()方法。

<!-- promise finally方法-->
const promise = new Promise((resolve, reject) => {})
.then(result => {})
.catch(err => {})
.finally(() => {})
复制代码

Promise.all()

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

<!-- promise.all方法-->
const promise1 = new Promise((resolve, reject) => {resolve("promise1")}),
     promise2 = new Promise((resolve, reject) => {resolve("promise2")}),
     promise3 = new Promise((resolve, reject) => {resolve("promise3")});
     
Promise.all([promise1,promise2,promise3]).then(data => { 
  console.log(data); 
  // ["promise1", "promise2", "promise3"] 结果顺序和promise实例数组顺序是一致的
}).catch(err => {
  consolo.log(err)
});
复制代码

只有promise一、promise二、promise3的状态都变成fulfilled,Promise.all的状态才会变成fulfilled,此时promise一、promise二、promise3的返回值组成一个数组,传递给Promise.all的回调函数。

只要promise一、promise二、promise3之中有一个被rejected,Promise.all的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给Promise.all的回调函数

在作项目的时候咱们常常会碰到一个页面要有多个请求,咱们可使用promise.all封装,便于请求管理。

相似的axios也有axios.all()方法处理并发请求

Promise.race()

Promise.race方法一样是将多个 Promise 实例,包装成一个新的 Promise 实例。

<!-- promise.race方法-->
const promise = Promise.race([promise1, promise2, promise3]);
复制代码

上面代码中,只要promise一、promise二、promise3之中有一个实例率先改变状态,promise的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

3、Promise的应用

与axios结合

项目中咱们常常会遇到须要根据业务将axios再封装的,好比请求拦截设置token以及Content-Type,响应拦截根据不一样的状态码设置不一样的响应。此外咱们还能够将axios再封装

import axios from "./axios"
import qs from "qs";
export default {
    get: function(url, params) {
      return new Promise((resolve, reject) => {
        axios.get(url, {params: params})
          .then(res => {
            resolve(res)
          })
          .catch(err => {
           console.log(err)
          })
      })
    },
    post: function(url, params) {
      return new Promise((resolve, reject) => {
        axios.post(url, qs.stringify(params))
          .then(res => {
            resolve(res);
          })
          .catch(err => {
              console.log(err)
          })
      });
    }
}
<!--使用 整个user模块的请求都在此文件管理-->
import require from "@/utils/require"
const user = {
    userList() {
      return require.post("/api.php", {}).then(res => res.result)
    },
    userInfo() {
      return require.post("/api.php?&uid=20", {}).then(res => res.result)
    },
    ...
}
export default user
复制代码

异步加载图片

用promise实现异步加载图片的例子

function loadImageAsync(url) {
    return new Promise((resolve, reject) => {
      const image = new Image();
      image.onload = () => {
        resolve(image);
      };
      image.onerror = () => {
        reject(new Error('Could not load image at ' + url));
      };
      image.src = url;
    });
}
const loadImage = loadImageAsync(url);
复制代码

写在最后

前端技术近年来发展迅速,新技术层出不穷,做为一个程序员er也要有持续学习的觉悟。 欢迎你们关注个人公众号:前端Readhub 。😄😄

相关文章
相关标签/搜索