Promise的使用方法

108 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第八天,点击查看活动详情

1.jpg

最近使用promise比较多,这里总结一下它的用法
祝愿看到文章的朋友身体健康;如果可以麻烦一键三连

Promise

Promise诞生的原因

  • Promise诞生之前,我们处理异步请求时,通常在回调函数中处理
  • 若需要执行多个异步请求,每一个请求又依赖上一个请求的结果
    • “回调地狱”
    • 可读性差
    • 耦合度高,难以维护和复用
    • 回调函数都是匿名函数,不方便调试
// 第一个请求
$.ajax({
  url:'url1',
  success:function(){
    // 第二个请求
    $.ajax({
      url:'url2',
      success:function(){
        // 第三个请求
      }
    })
  }
})

Promise的生命周期

  • pending(进行中)、fulfilled(已成功)和rejected(已失败)
  • Promise在创建时处于pending状态,状态的改变只有两种可能
    • 一种是在Promise执行成功时,由pending状态改变为fulfilled状态
    • 一种是在Promise执行失败时,由pending状态改变为rejected状态
  • Promise状态只改变一次

Promise的基本用法

  • Promise本身是一个构造函数
  • Promise执行的过程是:
    • 在接收的函数中处理异步请求,然后判断异步请求的结果
    • 若结果为“true”,则表示异步请求执行成功,调用resolve()函数,resolve()函数一旦执行,Promise的状态就从pending变为fulfilled
    • 若结果为“false”,则表示异步请求执行失败,调用reject()函数,reject()函数一旦执行,Promise的状态就从pending变为rejected
    • resolve()函数和reject()函数可以传递参数,作为后续.then()函数或者.catch()函数执行时的数据源

Promise在创建后会立即调用,然后等待执行resolve()函数或者reject()函数来确定Promise的最终状态

let promise = new Promise(function(resolve,reject){
  console.log('promise1');
  resolve();
  console.log('promise2');
})
promise.then(function(){
  console.log('resolve');
})
console.log('hello');
//首先是Promise的创建,会立即执行,输出“promise1”,“promise2”
//然后是执行resolve()函数,这样的话就会触发then()函数指定回调函数的执行,但是它需要等当前线程中的所有同步代码执行完毕,因此会先执行最后一行同步代码,输出“hello”
//最后是当所有同步代码执行完毕后,执行then()函数,输出“resolve”


// 输出顺序为:promise1、promise2、hello、resolve

所以有了promise之后,ajax请求可以实现为

let ajaxRequest1 = new Promise(function(resolve,reject){
  $.ajax({
    url:'url',
    success:function(){
      resolve();
    }
  })
})
ajaxRequest1.then(function(){
  console.log('resolve');
})

Promise中的函数

then()

  • Promise在原型属性上添加了一个then()函数,在Promise实例状态改变时执行的回调函数
  • 接收两个函数作为参数
    • 第一个参数为Promise执行成功后调用resolve()函数时函数传递的参数
    • 第二个参数是可选的,表示的是Promise在执行失败后(即调用了reject()函数或抛出了异常),执行的回调函数
  • then()函数返回的是一个新的Promise实列
    • 可以使用链式调用
    • 上一轮then()函数内部return的值会作为下一轮then()函数接收的参数值
  • then()函数中不能返回Promise实例本身,否则会出现Promise循环引用的问题,抛出异常
const promise = new Promise((resolve,reject) =>{
  resolve(1);
  reject(2);
});
promise.then((result)=>{
  console.log(result);//1
  return 3
},(result)=>{
  // 不会输出,因为执行resolve()函数改变了Promise的状态,Promise的状态一旦改变,就会永久保持该状态,不会再次改变
  console.log(result);
}).then((result)=>{
  console.log(result);//3
});


const promise = new Promise((resolve,reject) =>{
  reject(2);
  resolve(1);
});
promise.then((result)=>{
  // 不会输出,因为执行reject()函数改变了Promise的状态,Promise的状态一旦改变,就会永久保持该状态,不会再次改变
  console.log(result);
  return 3
},(result)=>{
  console.log(result);// 2
}).then((result)=>{
  console.log(result);//undefined
})

catch()

  • 虽然then()函数里面也能处理rejected状态的Promise的回调函数,但是不推荐
  • catch()函数是Promise执行失败之后的回调,接收的参数是reject()函数传递的参数
  • 在Promise执行过程中出现了异常,会自动触发reject(err),不需要手动调用
const promise = new Promise((resolve,reject) =>{
  null.name;
});
promise.catch((err)=>{
  console.log(err);
  //TypeError: Cannot read properties of null (reading 'name')
})

Promise.all()

  • then()函数和catch()函数是Promise原型链中的函数,因此每个Promise的实例可以进行共享
  • all()函数是Promise本身的静态函数,用于将多个Promise实例包装成一个新的Promise实例
const promise1 = new Promise((resolve,reject) =>{
  null.name;
});
const promise2 = new Promise((resolve,reject) =>{
  resolve(1);
});
const promise3 = new Promise((resolve,reject) =>{
  resolve(2);
});
const allPromise = Promise.all([promise1,promise2,promise3]);
allPromise.then((res)=>{
  console.log(res);
}).catch((res)=>{
  console.log(res);
  //TypeError: Cannot read properties of null (reading 'name')
})


const promise1 = new Promise((resolve,reject) =>{
  resolve(1);
});
const promise2 = new Promise((resolve,reject) =>{
  resolve(1);
});
const promise3 = new Promise((resolve,reject) =>{
  resolve(2);
});
const allPromise = Promise.all([promise1,promise2,promise3]);
allPromise.then((res)=>{
  console.log(res);//[1,1,2]
}).catch((res)=>{
  console.log(res);
})
  • 只有promise1、2、3全部的状态都变为fulfilled成功状态,allPromise的状态才会变为fulfilled状态
    • promise1、2、3的返回值组成一个数组,作为allPromise的then()函数的回调函数的参数
  • 只要promise1、2、3中有任意一个状态变为rejected失败状态,allPromise的状态就变为rejected状态
    • 第一个被reject的实例的返回值会作为allPromise的catch()函数的回调函数的参数
  • 如果在实例promise1、2、3中定义了catch函数,当其中一个Promise状态变为rejected时,不会触发Promise.all()函数的catch()函数
const promise1 = new Promise((resolve,reject) =>{
  null.name;
}).catch(err => err);
const promise2 = new Promise((resolve,reject) =>{
  resolve(1);
});
const promise3 = new Promise((resolve,reject) =>{
  resolve(2);
});
const allPromise = Promise.all([promise1,promise2,promise3]);
allPromise.then((res)=>{
  console.log(res);
  //[TypeError: Cannot read properties of null (reading 'name'), 1, 2]
}).catch((res)=>{
  console.log(res);
})

Promise.race()

  • Promise.race()函数作用于多个Promise实例上,返回一个新的Promise实例
  • 表示的是如果多个Promise实例中有任何一个实例的状态发生改变,那么这个新实例的状态就随之改变
  • 最先改变的那个Promise实例的返回值将作为新实例的回调函数的参数

Promise.resolve()

  • Promise提供了一个静态函数resolve(),用于将传入的变量转换为Promise对象
  • 等价于在Promise函数体内调用resolve()函数
  • Promise.resolve()函数执行后,Promise的状态会立即变为fulfilled,然后进入then()函数中做处理

Promise.reject()

  • 等价于在Promise函数体内调用reject()函数

Promise用法

  • Promise里面的代码和同步代码一起执行
let promise = new Promise(function(resolve,reject){
  console.log('1');
  resolve();
  console.log('2');
})
promise.then(function(){
  console.log('3');
})
console.log('4');
// 1 2 4 3
  • 同一个Promise函数内只能执行一次resolve()函数或者reject()函数
let promise = new Promise(function(resolve,reject){
  console.log('1');
  reject();
  resolve();
})
promise.then(function(){
  console.log('2');
})
console.log('3');
// 1 3
// 2不会打印,因为执行reject()函数后Promise状态已经改变不能再次改变
  • 同一个Promise实例自身重复执行
  • 在then()函数中只要不是抛出错误的都会继续执行then()函数
let promise = new Promise(function(resolve,reject){
  console.log('1');
  resolve();
})
promise.then(function(){
  console.log('2');
  return new Error('error')
}).then((res)=>{
  console.log('3');
  console.log(`then:${res}`)
}).catch((res)=>{
  console.log('4');
  console.log(`catch:${res}`)
})
// 1
// 2
// 3
// then:Error: error


let promise = new Promise(function(resolve,reject){
  console.log('1');
  resolve();
})
promise.then(function(){
  console.log('2');
  return null.name
}).then((res)=>{
  console.log('3');
  console.log(`then:${res}`)
}).catch((res)=>{
  console.log('4');
  console.log(`catch:${res}`)
})
// 1
// 2
// 4
// catch:TypeError: Cannot read properties of null (reading 'name')
  • then()函数接收的参数如果不是函数,会产生值穿透现象
Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log);
// 1
//第一个then()函数接收一个值“2”,第二个then()函数接收一个Promise,都不是函数形式,因此这两个then()函数会发生值穿透现象
// 第三个then()函数因为接收到console.log()函数,因此会执行,此时接收的是最开始的resolve(1)的值,最终会输出“1”