网站首页 > 编程文章 正文
Promise对象是一个构造函数,用来生成Promise实例
- 当使用promise时,会传入一个执行器,执行器传入两个函数(resolve, reject),此执行器是立即执行的;
- 有三种状态:成功(fulfilled),失败(rejected),等待(pending);
- 执行器调用resolve走成功态,如果调用reject或发生异常,走失败态;如果执行器抛异常,走失败态;
- promise状态一旦改变,以后不能更改。
new Promise((resolve, reject) => {
resolve('ok')
})
原型方法
Promise.prototype.then
then() 方法返回一个新的Promise实例。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。
// 成功的情况
new Promise((resolve, reject) => {
resolve('ok')
}).then((value) => {
console.log(value) // ok
})
// 失败的情况
new Promise((resolve, reject) => {
reject('error')
}).then(null, (resson) => {
console.log(resson) // error
})
链式调用:
- 情况1:then后面直接.then
new Promise((resolve, reject) => {
resolve('ok')
}).then().then().then((value) => {
console.log(value) // ok
})
- 情况2:then返回一个常量
new Promise((resolve, reject) => {
resolve('ok')
})
.then(value => {
return 'okok'
})
.then(value => {
console.log(value) // 'okok'
})
- 情况3:then返回一个Promise
new Promise((resolve, reject) => {
resolve('ok')
})
.then(value => {
return new Promise((resolve, reject) => {
resolve('okok')
})
})
.then(value => {
console.log(value) // 'okok'
})
Promise.prototype.catch
catch() 用于注册一个在 promise 被拒绝时调用的函数,返回一个新的Promise实例。此方法是Promise.prototype.then(undefined, onRejected)的一种简写形式。
new Promise((resolve, reject) => {
reject('error')
}).catch(reason => {
console.log(reason) // error
})
同样支持链式写法
new Promise((resolve, reject) => {
reject('error')
}).catch().catch().catch(reason => {
console.log(reason) // error
})
catch与then情况一样:
- 不返回值或返回常值,promise状态为成功
- 产生异常(比如手动throw),promise状态为失败
- 返回一个promise,状态就是返回的promise的状态
Promise.prototype.finally
finally() 方法返回一个 Promise。在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。
这为在 Promise 是否成功完成后都需要执行的代码提供了一种方式,这避免了同样的语句需要在 then() 和 catch() 中各写一次的情况。
静态方法
Promise.resolve()
将现有对象转为 Promise 对象。方法也会返回一个新的 Promise 实例。
const p1 = Promise.resolve('p1-resolve')
p1.then((val) => {
console.log(val)
})
Promise.resolve().then(() => {
return Promise.resolve('123')
}).then((val) => {
console.log(val) // 123
})
- 参数是一个 Promise 实例 如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
const p1 = new Promise((resolve, reject) => {
resolve('abc')
})
const p2 = Promise.resolve(p1)
console.log(p1 === p2) // true
p2.then((value) => {
console.log(value)
})
- 参数是一个thenable对象 thenable对象指的是具有then方法的对象,比如下面这个对象。
const thenable = {
then: function(resolve, reject) {
resolve('abc')
}
};
Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。
const thenable = {
then: function(resolve, reject) {
resolve('abc')
}
}
const p1 = Promise.resolve(thenable).then((val) => {
console.log(val)
})
Promise.reject()
Promise.reject() 方法返回一个带有拒绝原因的 Promise 对象。
Promise.reject(new Error('err')).catch(e => {
console.log(e.message) // err
})
Promise.race()
Promise.race(iterable) 该方法返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成或失败,这要取决于第一个完成的方式是两个中的哪个。
const delay = (ms, msg) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(msg)
}, ms)
})
}
const p1 = delay(1500, 'p1')
const p2 = delay(2000, 'p2')
const p3 = delay(1000, 'p3')
Promise.race([p1, p2, p3]).then(value => {
console.log(value) // p3
})
Promise.all()
Promise.all()将多个Promise放在一个数组中,当整个数组的全部promise成功时才会返回成功,当数组中的promise有一个出现失败时就返回失败 (失败的原因是第一个失败promise的结果)。
const delay = (ms, msg) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(msg)
}, ms)
})
}
const p1 = delay(1500, 'p1')
const p2 = delay(2000, 'p2')
const p3 = delay(1000, 'p3')
Promise.all([p1, p2, p3]).then(value => {
console.log(value) // 2s后打印 [ 'p1', 'p2', 'p3' ]
})
Promise.allSettled
返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。
const delay = (ms, msg, isReject = false) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
isReject ? reject(msg) : resolve(msg)
}, ms)
})
}
const p1 = delay(1500, 'p1')
const p2 = delay(2000, 'p2')
const p3 = delay(1000, 'p3', true)
Promise.allSettled([p1, p2, p3]).then(value => {
console.log(value) // 2s后打印
/**
[
{ status: 'fulfilled', value: 'p1' },
{ status: 'fulfilled', value: 'p2' },
{ status: 'rejected', reason: 'p3' }
]
*/
})
Promis.any()
ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。
只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。\
Promise.any()跟Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。
const delay = (ms, msg, isReject = false) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
isReject ? reject(msg) : resolve(msg)
}, ms)
})
}
const p1 = delay(1500, 'p1')
const p2 = delay(2000, 'p2')
const p3 = delay(1000, 'p3', true)
Promise.any([p1, p2, p3]).then(value => {
console.log(value) // 2s后打印: p1
})
应用
接口请求超时
使用Promise.race来实现
// 模拟请求
function request(params) {
return new Promise(resolve => {
setTimeout(() => {
resolve(params)
}, 3000)
})
}
/**
* 超时包装函数
* @param {() => Promise<any>} p 请求函数
* @param {number} timeout 超时时间
* @returns
*/
function timeoutWrap(p, timeout) {
const delay = new Promise((_, reject) => {
setTimeout(() => {
reject('超时了')
}, timeout)
})
return (...args) => {
const p1 = p(...args)
const res = Promise.race([p1, delay])
return res
}
}
const r1 = timeoutWrap(request, 1000)({ id: 1 })
r1.then(response => {
console.log(response)
}).catch(e => {
console.log('e', e)
})
控制并发
之前在开发大文件上传的时候,文件很大的时候,会分成较多的切片,如果此时将全部切片一次性上传,并发就会很大。那我们能不能控制一下,比如一次只发6个请求,某一个请求完了,就让第7个补上,又请求完了,让第8个补上,以此类推,让最高并发量变成可控的。
我们先来模拟一下并发很多会怎么样,如下面的例子:
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
function request(i = 0) {
const r = Date.now() + Math.floor(Math.random() * 1000)
return axios({
method: 'get',
url: `http://127.0.0.1:8088/abc?s=${r}`
})
}
// 100个并发
function run() {
const count = 100
for (let i = 0; i < count; i++) {
request(i)
}
}
run()
</script>
情况如下图:此时会有大量的请求挂起
解决的思路,是使用并发池,并发池设置一个最大值,超过最大值就等待:
function run() {
const maxPoolsSize = 6 // 最大并发数
const count = 100
let pools = [] // 并发池
for (let i = 1; i <= count; i++) {
const task = request(i)
task.then(response => {
// 执行完成,从池中移除
const idx = pools.findIndex(x => x === task)
pools.splice(idx, 1)
})
pools.push(task)
if (pools.length >= maxPoolsSize) {
// 等待并发池执行完一个任务后
await Promise.race(pools)
}
}
}
进一步封装:
class Scheduler {
constructor(limit = 6) {
this.limit = limit
this.tasks = []
}
addTask(task, ...args) {
this.tasks.push({
task,
...args
})
}
async startTask() {
const pools = []
for (let i = 0; i < this.tasks.length; i++) {
let { task, ...args } = this.tasks[i]
task = task(args)
task.then(response => {
// 执行完成,从池中移除
const idx = pools.findIndex(x => x === task)
pools.splice(idx, 1)
})
pools.push(task)
if (pools.length >= this.limit) {
// 等待并发池执行完一个任务后
await Promise.race(pools)
}
}
}
}
// 使用
const scheduler = new Scheduler()
for (let i = 0; i < 100; i++) {
scheduler.addTask(request, i)
}
scheduler.startTask()
重复请求问题
比如:tab切换,接口返回时长不确定数据紊乱。
这个问题有两个解决方案,一个是接口数据舍弃,另一个是取消请求。
我们来看重复取消的问题:
<script setup lang="ts">
import { ref } from 'vue'
import axios from 'axios'
function defRequest(params = {}) {
const r = Date.now() + Math.floor(Math.random() * 1000)
return axios({
method: 'get',
url: `http://127.0.0.1:8088/abc?r=${r}`,
params
})
}
const showData = ref('')
const onClick = (id: number) => {
defRequest({ id }).then(() => {
showData.value = id + ''
})
}
</script>
<template>
<div>
<button @click="onClick(1)">类型1</button>
<button @click="onClick(2)">类型2</button>
<button @click="onClick(3)">类型3</button>
<p>数据:{{ showData }}</p>
</div>
</template>
<style lang="scss" scoped></style>
我们借助Promise.race来实现
class CancelPromise {
pendingPromise: Promise<any> | null = null
reject: any
request(requestFn: Promise<any>) {
if (this.pendingPromise) {
this.cancel('取消了请求')
}
const cancelPromise = new Promise((_, reject) => (this.reject = reject))
this.pendingPromise = Promise.race([cancelPromise, requestFn])
return this.pendingPromise
}
cancel(reason: string) {
this.reject(reason)
this.pendingPromise = null
}
}
function defRequest(params = {}) {
const r = Date.now() + Math.floor(Math.random() * 1000)
return axios({
method: 'get',
url: `http://127.0.0.1:8088/abc?r=${r}`,
params
})
}
const cancelPromise = new CancelPromise()
const showData = ref('')
const onClick = (id: number) => {
cancelPromise.request(defRequest({ id })).then(() => {
showData.value = id + ''
})
}
- 上一篇: 在 JS 中如何使用 Ajax 来进行请求
- 下一篇: 第6章 Vue组件高级(vue组件用法)
猜你喜欢
- 2024-09-11 第39章 Django调用验证码(调用验证码接口)
- 2024-09-11 第6章 Vue组件高级(vue组件用法)
- 2024-09-11 前后端分离 Vue + Springboot 实现用户列表单页面开发(建议收藏)
- 2024-09-11 在 JS 中如何使用 Ajax 来进行请求
- 2024-09-11 express框架开发开始篇 方便以后写项目时候可拿过来直接使用
- 2024-09-11 Spring Cloud Alibaba 之 Gateway 服务网关跨域问题
- 2024-09-11 SpringBoot+Vue+Flowable,模拟一个请假审批流程
- 2024-09-11 Vue基础(2)(vue基础笔试题)
- 2024-09-11 vue.js快速入门(vue.js入门教程)
- 2024-09-11 每日优鲜供应链前端团队微前端改造
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- spire.doc (59)
- system.data.oracleclient (61)
- 按键小精灵源码提取 (66)
- pyqt5designer教程 (65)
- 联想刷bios工具 (66)
- c#源码 (64)
- graphics.h头文件 (62)
- mysqldump下载 (66)
- sqljdbc4.jar下载 (56)
- libmp3lame (60)
- maven3.3.9 (63)
- 二调符号库 (57)
- 苹果ios字体下载 (56)
- git.exe下载 (68)
- diskgenius_winpe (72)
- pythoncrc16 (57)
- solidworks宏文件下载 (59)
- qt帮助文档中文版 (73)
- satacontroller (66)
- hgcad (64)
- bootimg.exe (69)
- android-gif-drawable (62)
- axure9元件库免费下载 (57)
- libmysqlclient.so.18 (58)
- springbootdemo (64)
本文暂时没有评论,来添加一个吧(●'◡'●)