ES6新增对象 - Promise使用方法详解

目录

一、什么是Promise

1.1 Promise的三种状态

二、Promise 基本用法

2.1 Promise基本使用

2.2 Promise使用时传参

2.3 Promise 链式调用

2.4 链式调用注意事项

三、Promise内置方法

3.1 Promise.all()

3.2 Promise.race()

3.3 Promise.allSettled()

3.4 Promise.finally()

3.5 Promise.any()


一、什么是Promise

A Promise is an object representing the eventual completion or failure of an asynchronous operation.

从官方文档我们可知

  • Promise是ES6新增的一个对象,一个构造函数。
  • 用于多层次异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数地狱问题。

promise主要是用来解决回调地狱的问题,通过使用.then来使得代码成链式调用,方便维护和使用。.then中的回调函数属于异步任务中的微任务。

1.1 Promise的三种状态

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。——注:fulfilled 或称 resolved
  • rejected: 意味着操作失败。

什么意思呢?三种状态对应异步操作的三种情况,比如发送一次ajax请求,初始等待时,状态为pending;ajax请求成功时,调用Promise内置函数resolve(), Promise状态 => fulfilled;请求失败时,调用函数reject(),Promise状态 => rejected。

Promise是异步编程的一种解决方案,它的构造函数是同步执行的,then 方法是异步执行的,所以Promise创建后里面的函数会立即执行,构造函数中的resolve和reject只有第一次执行有效,也就是说Promise状态一旦改变就不能再变

二、Promise 基本用法

var Promise: PromiseConstructor
new <any>(executor: (resolve: (value?: any) => void, reject: (reason?: any) => void) => void) => Promise<any>

从语法上来看,Promise 是一个构造函数,需要传入一个参数 executor函数。

new Promise 时会调用 executor 函数, executor 函数也有两个参数 resolve 和 reject,这两个参数也是函数,调用 resolve 或 reject 时,分别将promise的状态改为fulfilled(完成)或 rejected(失败)。Promise 状态只能唯一。

2.1 Promise基本使用

下面来看两个例子

(1) 用流程控制的分支结构,判断异步请求是否成功

var promise = new Promise((resolve, reject) => {
  if(异步请求成功){
    resolve()
  }else{
    reject()
  }
})

promise.then(()=>{
  //success
},()=>{
  //failure
})

(2) 用定时器setTimeout模拟异步请求成功

function ajax() {
  return new Promise(resolve => {
    setTimeout(resolve, 1000)
  })
}

ajax().then(() => {
  console.log("request success") //success
})

上面代码表示,如果异步操作成功,就调用resolve()方法,就会执行Promise实例的then()方法的第一个回调函数,如果失败则调用Promise实例的then()方法的第二个回调函数。

2.2 Promise使用时传参

resolve() 和 reject() 函数调用时可传参,传入的参数会被Promise实例的 then 和 catch 方法捕获。

var promise = new Promise((resolve, reject) => {
  if (异步请求成功) {
    resolve("success")
  } else {
    reject("error")
  }
})

promise.then( res => {
    res // success
})
//捕获异常可用catch()方法
promise.catch( err => {
    err // error
})

我们来执行一下,看看结果:

2.3 Promise 链式调用

Promise强大的地方在于此,如果发送一个异步请求,又返回另外一个异步请求时,可用链式调用

new Promise((resolve) => {
  resolve(1)
}).then((res) => {
  return new Promise((resolve) => {
    resolve(res+2)
  })
}).then((res) => {
  return new Promise((resolve) => {
    resolve(res+3)
  })
}).then((res) => {
  return res // 6
})

当对异步请求返回结果 res 的简单操作时,可用 Promise.resolve() 简写

new Promise((resolve) => {
  resolve(1)
}).then((res) => {
  return Promise.resolve(res+2)
}).then((res) => {
  return Promise.resolve(res+3)
}).then((res) => {
  return res // 6
})

还有一种更简单的语法糖

new Promise((resolve) => {
  resolve(1)
}).then((res) => {
  return res + 2
}).then((res) => {
  return res + 3
}).then((res) => {
  return res // 6
})

运行结果:

2.4 链式调用注意事项

链式调用(chaining)按序执行,有以下约定,使用时要多注意。

  • 在本轮 事件循环运行完成之前,回调函数是不会被调用的。
  • 即使异步操作已经完成(成功或失败),在这之后通过 then()添加的回调函数也会被调用
  • 通过多次调用then() 可以添加多个回调函数,它们会按照插入顺序执行

看下面的例子

new Promise((resolve) => {
  resolve()
}).then(() => {
  console.log('execute') // execute
}).then(() => {
  console.log('execute1') // execute
}).then(() => {
  console.log('execute2') // execute
})

我们来看看,运行结果:


只要触发了一次resolve(),链上的所有then都会被调用,当然后面的没有调用resolve自然拿不到操作数。

new Promise((resolve) => {
  resolve()
}).then(() => {
  console.log('execute') // execute
}).then(() => {
  console.log('execute1') // execute
}).catch(()=>{
  console.log('execute2') // no execute
}).then(()=>{
  console.log('execute3') // execute
})

运行结果:

注:中间穿插catch(),其后的then也会被执行


new Promise((resolve,reject) => {
  reject()
}).then(() => {
  console.log('execute') // no execute
}).catch(()=>{
  console.log('execute1') // execute
}).then(() => {
  console.log('execute2') // execute
})

运行结果:

注:捕获reject()后的then会被执行


触发resolve()之后,第一个then()执行,输出execute,接着抛出一个错误对象,中断程序的执行,接着catch()捕获错误信息对象(第二个then()不会被执行),即catch()被执行,输出execute2。—— 注:throw new Error(error),创造一个错误类型实例抛出;

关于throw new Error(error) 与 throw error

① throw new Error(error),是创建错误,创造一个错误类型抛出;

②throw error,这个是抛出错误。(不建议的写法)


throw语句的作用是手动中断程序执行,抛出一个错误。抛出错误一般都是与try catch 同时出现的。


注意,throw语句会中断程序的执行,导致throw语句后的语句无法正常执行

③ throw可以抛出任何类型的值,如: throw 42;

throw可以抛出任何类型的值,不仅仅是new Error() ,即throw new Error(),也就是说,它的参数可以是任何值。

new Promise((resolve) => {
  resolve()
}).then(() => {
  console.log("execute")  // execute
  throw new Error()
}).then(() => {
  console.log("execute1") // no execute
}).catch(() => {
  console.log("execute2") // execute
}).then(() => {
  console.log("execute3") // execute
})

运行结果:


情况有很多种,怎么去理解呢,看下面这个例子

const arr = ["foo","bar"]
arr.forEach(async (item) => {
  const res = await new Promise(resolve=>{
    resolve("why")
  }).then(res=>{
    return res
  })
  console.log(res);
  console.log(item);
// why、foo、why、bar
})

运行结果:

res 会拿到 Promise resolve()的操作数,输出结果为 why、foo、why、bar


return res 注释发现,仍然可以输出结果,相当于执行了 Promise.resolve(),catch()中同理

const arr = ["foo","bar"]
arr.forEach(async (item) => {
  const res = await new Promise(resolve=>{
    resolve("why")
  }).then(res=>{
    // return res 
    // Promise.resolve() 

  })
  console.log(res); // return res 注释掉,const res无返回值,自然就是undefined
  console.log(item);
// undefined、foo、undefined、bar
})

总结:then(),catch()触发后,会返回一个空的 resolve()


三、Promise内置方法

3.1 Promise.all()

Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。

Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 迭代器 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败 promise 的结果。

—— 全部fulfilled状态

var p1 = Promise.resolve("res1")
var p2 = Promise.resolve("res2")

Promise.all([p1, p2]).then((res) => {
  return res // ["res1", "res2"]
})

运行结果:

 —— 有rejected状态时

var p1 = Promise.reject("res1")
var p2 = Promise.resolve("res2")

Promise.all([p1, p2]).then((res) => {
  return res // res1
})

运行结果:

—— 捕获异常结果err为响应速度快的

var p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("err1")
  }, 1000)
})
var p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("err2")
  }, 2000)
})
Promise.all([p1, p2]).catch((err) => {
 return err // err1
})

运行结果:

3.2 Promise.race()

与Promise.all()对应,接受的也是数组,里面(数据项)也都是Promise实例。

Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回哪个结果,不管结果本身是成功状态还是失败状态。

1、只要有一个决议为成功或失败,新得到的Promise实例就相应地返回成功或失败,并把值传递过来。也就是说看决议哪个速度快,就返回的是谁

2、all传空数组会立刻决议为成功;而race传空数组会被挂起,它会没有任何反应

  • race 函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。
  • 如果传的迭代是空的,则返回的 promise 将永远等待。
  • 如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race 将解析为迭代中找到的第一个值。

示例1: 

var p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("res1")
  }, 3000)
})
var p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("err2")
  }, 2000)
})
Promise.race([p1, p2])
.then((res)=>{
  console.log(res);
}).catch((err)=>{
  console.log(err); //err2
})

运行结果:

注意:then,catch只会调用二者其一,并且取决于迭代器中参数的响应速度

示例2:

function getData1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('第一条数据加载成功');
      reject('err');
    }, 500);
  });
}

function getData2() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('第二条数据加载成功');
      resolve('data2');
    }, 1000);
  });
}

function getData3() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('第三条数据加载成功');
      resolve('data3');
    }, 1000);
  });
}

let p = Promise.race([getData1(),getData2(),getData3()]);

p.then(data => {
	console.log(data);
}, e => {
	console.log(e);
})

/*
第一条数据加载成功
err
第二条数据加载成功
第三条数据加载成功
*/

运行结果,如下动图演示:

Promise.race([Promise实例1,Promise实例2,Promise实例3]) 里面哪个结果获得的快,就返回哪个结果,不管结果本身是成功状态还是失败状态。

本例中getData1中的Promise实例1获得结果最快,所以race 函数返回一个 Promise实例的结果是getData1的结果,即err。

我的疑问???:race()函数最终返回的Promise实例的Promise Result值为什么是undefined,不应该是 err 吗?待解决......

网上查了一下,关于 [[PromiseResult]] : undefined 的问题,以下是给出的解决方案(不过,好像还是解决不了):
 

报错信息 [[PromiseResult]] : undefined 通常出现在JavaScript中处理Promise时。这个错误表明有一个Promise对象,但是它的结果属性 [[PromiseResult]] 未定义。

这个问题可能是因为:

  1. 你尝试获取一个尚未解决(fulfilled)的Promise的结果。

  2. 你可能在Promise内部抛出了一个错误,但是没有使用.catch()来处理这个错误,导致错误没有被捕获,并且Promise的状态变为未解决。

解决方法:

  1. 确保在尝试获取Promise结果之前,Promise已经被解决(即它的状态变为fulfilled)。

  2. 如果你在Promise中捕获错误,请确保你有一个.catch()来处理它们,或者你使用的是try/catch来处理可能抛出错误的代码。

  3. 使用Promise.resolve()Promise.reject()来创建一个已解决或已拒绝的Promise,从而避免[[PromiseResult]]是未定义的情况。

  4. 如果你在使用异步函数,请确保你正确地使用了await关键字来等待Promise解决。

示例代码:
 

// 确保Promise已解决
let promise = new Promise((resolve, reject) => {
  resolve('Promise resolved!');
});
 
promise.then((result) => {
  console.log(result); // 输出: 'Promise resolved!'
}).catch((error) => {
  console.error(error); // 处理错误
});
 
// 使用try/catch处理可能的错误
async function asyncFunction() {
  try {
    let result = await promise;
    console.log(result);
  } catch (error) {
    console.error(error);
  }
}

以上示例展示了如何处理Promise的结果,以及如何捕获并处理潜在的错误。


总结race: 竞技/竞赛

Promise.race()   接受多个Promise对象 数组形式传递参数

只要有一个Promise状态改变, 那么就结束

以先拿到结果Promise值作为整个值返回

如果最先返回的值状态是fulfilled,那就进入then中结束

如果最先返回的值状态是rejected,那就进入catch中结束

—— 全部fulfilled状态

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(11111)
  }, 3000);
})
 
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(22222)    //最先结束,成为结果
  }, 500);
})
 
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)
  }, 1000);
})
 
Promise.race([p1, p2, p3]).then(res => {
  console.log("res:", res)    //res: 22222
}).catch(err => {
  console.log("err:", err)   
})

—— 有rejected状态时

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(11111)
  }, 3000);
})
 
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(22222)     // 错误状态,成为结果
  }, 500);
})
 
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)
  }, 1000);
})
 
// race: 竞技/竞赛
// 只要有一个Promise变成fulfilled状态, 那么就结束
Promise.race([p1, p2, p3]).then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)   //err: 22222
})

3.3 Promise.allSettled()

Promise.allSettled()方法返回一个在所有给定的promise已被决议或被拒绝后决议的promise,并带有一个对象数组,每个对象表示对应的promise结果。

示例1:

var p1 = Promise.resolve("res1")
var p2 = Promise.reject("res2")

Promise.allSettled([p1, p2]).then((res) => {
    return res   // [{status: "fulfilled", value: "res1"}, {status: "rejected", reason: "res2"}]
})

返回一个对象数组,包含iterator所有参数Promise的状态和结果。

运行结果:

该方法参数也是和 .all 相同。顾名思义,这个方法是等所有promise参数确定状态后,才会执行回调函数,不管是成功的状态还是拒绝的状态,都等待全部执行后,并返回一个包含每个 Promise 解决状态的对象数组,每个对象包含两个属性:status 和 value;state表示promise的状态:resolve和rejected,value代表的是promise传递的值。

请注意,Promise.allSettled 是 ES2020(也称为 ES11)中引入的新方法,需要支持该版本的 JavaScript 运行环境才能使用

示例2:

var p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log("----打印:p1");
    resolve("p1--3000");
  }, 3000);
});
 
var p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("p2--1000");
  }, 1000);
});
 
var p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("p3--500");
  }, 500);
});
 
var p4 = new Promise((resolve, reject) => {
  throw new Error("抛出错误");
});
 
Promise.allSettled([p1, p2, p3, p4])
  .then((result) => {
    console.log("----打印:result", result);
  })
  .catch((err) => {
    console.log("----打印:", err); //不执行
  });
 
//执行结果
// ----打印:p1
// ----打印:result [
//   { status: 'fulfilled', value: 'p1--3000' },
//   { status: 'rejected', reason: 'p2--1000' },
//   { status: 'fulfilled', value: 'p3--500' },
//   {
//     status: 'rejected',
//     reason: Error: 抛出错误
//   }
// ]

运行结果:

3.4 Promise.finally()

Promise.finally方法的回调函数不接受任何参数,这表明,finally方法里面的操作,应该是与Promise状态无关的,无论 Promise 的状态如何,onFinally 回调都会被执行。它不接收任何参数,也没有返回值。这意味着它主要用于清理和最终处理逻辑,而不关心 Promise 的解决结果或拒绝原因。 

var p1 = new Promise((resoleve, reject) => {
  setTimeout(() => {
    resoleve("p1--3000");
  }, 3000);
});
 
p1.then((res) => {
  console.log("----打印:", res);
}).finally(() => {
  console.log("----打印:调用了");
});
 
//执行结果
// ----打印: p1--3000
// ----打印:调用了

var p2 = new Promise((resoleve, reject) => {
  setTimeout(() => {
    reject("p2--1000");
  }, 1000);
});
 
p2.then((res) => {})
  .catch((err) => {
    console.log("----打印:", err);
  })
  .finally(() => {
    console.log("----打印:也调用了");
  });
 
// 执行结果
// ----打印: p2--1000
// ----打印:也调用了

运行结果:

3.5 Promise.any()

Promise.any接收一个promise的数组作为参数,只要其中有一个Promise成功执行,就会返回已经成功执行的Promise的结果;若全部为rejected状态,则会到最后的promise执行完,全部的promise返回到异常函数中;可用于多通道获取数据,谁先获取就执行下一步程序,跳出这个过程。这和all的相反。

var p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("p1--3000");
  }, 3000);
});
var p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("p2--1000");
  }, 1000);
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log("----打印p3");
    resolve("p3--5000");
  }, 5000);
});
 
var promiseArr = [p1, p2, p3];
console.time("promiseArr");
Promise.any(promiseArr)
  .then((res) => {
    console.log("res", res); //res [ 'p1--3000', 'p2--1000', 'p3--5000' ]
    console.timeEnd("promiseArr"); // promiseArr: 5.020s
  })
  .catch((err) => console.log(err));
 
//输出顺序 --虽然p2已经执行完,但是为rejected状态,而any会返回第一个resolve状态的对象
//   res p1--3000
// promiseArr: 3.009s
// ----打印p3
 
//另外一种情况
var p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("p1--3000");
  }, 3000);
});
var p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("p2--1000");
  }, 1000);
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log("----打印p3");
    reject("p3--5000");
  }, 5000);
});
 
var promiseArr = [p1, p2, p3];
console.time("promiseArr");
Promise.any(promiseArr)
  .then((res) => {
    console.log("res", res); //res [ 'p1--3000', 'p2--1000', 'p3--5000' ]
    console.timeEnd("promiseArr"); // promiseArr: 5.020s
  })
  .catch((err) => console.log(err));
 
//输出结果   解释--因为p1,2,3都是错误,所以any一直在等有成功的状态,所以知道p3结束后,没有成功的,就走catch那边
// ----打印p3
// [AggregateError: All promises were rejected] {
//   [errors]: [ 'p1--3000', 'p2--1000', 'p3--5000' ]
// }

运行结果:

场景一

场景二 

参考:Promise详解大全:介绍、九个方法使用和区别、返回值详解 | Promise链式调用解惑


参考资料

 —— 整合总结ES6中常用的新特性:promise对象 ——

Promise详解大全:九个方法使用和区别、返回值详解 | Promise链式调用解惑

JS-Promise用法总结 - CSDN博客 | JS中的promise用法详解JS的promise用法 - CSDN博客

如何使用 Promise - 学习 Web 开发 | MDN | JavaScript基础之Promise - 知乎

Javascript中Promise的四种常用方法总结_javascript技巧_脚本之家

  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

儒雅的烤地瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值