ECMAScript--promise的使用

news/2025/1/30 16:10:21/

​ 一、Promise的简介

Promise是一个代理,它所代表的值在创建时并不一定是已知的。借助Promise,我们能够将处理程序与异步操作最终的成功值或者失败原因关联起来。这一特性使得异步方法可以像同步方法那样返回值,不同之处在于异步方法不会立即返回最终值,而是返回一个Promise,以便在未来某个时刻提供该值。

详细讲解:Promise - JavaScript | MDN

Promise必然处于以下几种状态之一:

待定(pending):这是初始状态,此时Promise既没有被兑现,也没有被拒绝。

已兑现(fulfilled):表明操作成功完成。

已拒绝(rejected):意味着操作失败。

处于待定状态的Promise,最终状态要么是已兑现并返回一个值,要么是已拒绝并返回一个原因(错误)。一旦出现这两种情况中的任意一种,通过Promise的then方法串联起来的处理程序就会被调用。即便在绑定相应处理程序时,Promise已经处于已兑现或已拒绝状态,处理程序也会被立即调用,所以在异步操作完成和绑定处理程序之间不存在竞态条件。 如果一个Promise已经被兑现或拒绝,即不再处于待定状态,那么就称这个Promise----已敲定(settled)。

在这里插入图片描述

我们可以通过打印的形式,简单了解一下promise

在这里插入图片描述

二、异步操作

Promise,译为承诺,是 异步编程 的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大

在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码

doSomething(function(result) {doSomethingElse(result, function(newResult) {doThirdThing(newResult, function(finalResult) {console.log('得到最终结果: ' + finalResult);}, failureCallback);}, failureCallback);
}, failureCallback);

多次异步调用,形成了经典的回调地狱

现在通过Promise的改写上面的代码

doSomething().then(function(result) {return doSomethingElse(result);
})
.then(function(newResult) {return doThirdThing(newResult);
})
.then(function(finalResult) {console.log('得到最终结果: ' + finalResult);
})
.catch(failureCallback);

三、promise中的方法

Promise 并发相关的静态方法

Promise 类提供了四个静态方法来促进异步任务的并发

Promise.all():在所有传入的 Promise 都被兑现时兑现;若任意一个 Promise 被拒绝,它就会拒绝。
Promise.allSettled():在所有的 Promise 都被敲定时兑现。
Promise.any():只要任意一个 Promise 被兑现,它就会兑现;只有当所有的 Promise 都被拒绝时,它才会拒绝。
Promise.race():在任意一个 Promise 被敲定时敲定。也就是任意一个 Promise 被兑现时它就兑现,任意一个 Promise 被拒绝时它就拒绝。
这些方法都有以下特点:

都接受一个 Promise(确切地说是 thenable)的可迭代对象,并返回一个新的 Promise。
都支持子类化,即可以在 Promise 的子类上调用,结果会是一个属于子类类型的 Promise。
不过,子类的构造函数必须实现与 Promise() 构造函数相同的签名,也就是要接受一个以 resolve reject 回调函数作为参数的单个 executor 函数
同时,子类还必须有一个 resolve 静态方法,能像 Promise.resolve() 一样调用,用于将值解析为 Promise。

需要注意的是,JavaScript 本质上是单线程的,所以在任何时刻,只有一个任务会被执行。尽管控制权可以在不同的 Promise
之间切换,让 Promise 的执行看起来是并发的,但在 JavaScript 中,并行执行只能通过 worker 线程实现。

构造函数

Promise():用于创建一个新的 Promise 对象。这个构造函数主要用于封装还没有添加 promise 支持的函数。

静态属性

Promise[Symbol.species]:返回用于构造从 Promise 方法返回值的构造函数。

静态方法

Promise.all():接受一个 Promise 可迭代对象作为输入,然后返回单个 Promise。

当所有输入的 Promise 都兑现时(就算传入的可迭代对象为空),返回的 Promise会被兑现,其值是一个包含所有兑现值的数组
要是输入的任何 Promise 被拒绝,返回的 Promise也会被拒绝,并且返回第一个拒绝的原因。

Promise.allSettled():接受一个 Promise 可迭代对象作为输入,返回单个 Promise。

当所有输入的 Promise 都敲定时(包括传入的可迭代对象为空的情况),返回的 Promise 会兑现,其值是一个描述每个 Promise结果的对象数组

Promise.any():接受一个 Promise 可迭代对象作为输入,返回单个 Promise。

只要任何输入的 Promise 兑现,返回的 Promise 就会兑现,其值为第一个兑现的值
如果所有输入的 Promise都被拒绝(包括传入的可迭代对象为空时),返回的 Promise 将以带有一个包含拒绝原因的数组的 AggregateError 拒绝。

Promise.race():接受一个 Promise 可迭代对象作为输入,返回单个 Promise。

返回的 Promise 与第一个敲定的 Promise 的最终状态保持一致。

Promise.race():返回一个新的 Promise 对象,该对象会以给定的原因拒绝。
Promise.resolve():返回一个新的 Promise 对象,该对象会以给定的值兑现。

如果值是一个 thenable 对象(即具有 then 方法),返回的 Promise 对象会“跟随”该 thenable对象,采用其最终的状态;否则,返回的 Promise 对象会以该值兑现。

通常情况下,如果你不确定一个值是否为 Promise,最好使用 Promise.resolve(value) 将其转换成 Promise 对象,然后把返回值当作 Promise 来处理。

四、promise的用法

Promise对象是一个构造函数,用来生成Promise实例

const promise = new Promise(function(resolve, reject) {});

Promise接受一个函数作为参数,该函数的两个参数分别是resolvereject。这两个函数就是回调函数

resolve函数的作用:在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

reject函数的作用:在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

实例方法

Promise构建出来的实例存在以下方法:

then()
catch()
finally()
then()

then

then是实例状态发生改变时的回调函数,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数

then方法返回的是一个新的Promise实例,也就是promise能链式书写的原因

getJSON("/posts.json").then(function(json) {return json.post;
}).then(function(post) {// ...
});
catch

catch()方法是 .then(null, rejection) .then(undefined, rejection) 的别名,用于指定发生错误时的回调函数

getJSON('/posts.json').then(function(posts) {// ...
}).catch(function(error) {// 处理 getJSON 和 前一个回调函数运行时发生的错误console.log('发生错误!', error);
});

Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止

getJSON('/post/1.json').then(function(post) {return getJSON(post.commentURL);
}).then(function(comments) {// some code
}).catch(function(error) {// 处理前面三个Promise产生的错误
});

一般来说,使用catch方法代替then()第二个参数

Promise对象抛出的错误不会传递到外层代码,即不会有任何反应

const someAsyncThing = function() {return new Promise(function(resolve, reject) {// 下面一行会报错,因为x没有声明resolve(x + 2);});
};

浏览器运行到这一行,会打印出错误提示ReferenceError: x is not defined,但是不会退出进程

catch()方法之中,还能再抛出错误,通过后面catch方法捕获到

finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
all()

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

const p = Promise.all([p1, p2, p3]);

接受一个数组(迭代对象)作为参数,数组成员都应为Promise实例

实例p的状态由p1、p2、p3决定,分为两种:

1.只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数
2.只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法

const p1 = new Promise((resolve, reject) => {resolve('hello');
})
.then(result => result)
.catch(e => e);const p2 = new Promise((resolve, reject) => {throw new Error('报错了');
})
.then(result => result)
.catch(e => e);Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]

如果p2没有自己的catch方法,就会调用Promise.all()catch方法

const p1 = new Promise((resolve, reject) => {resolve('hello');
})
.then(result => result);const p2 = new Promise((resolve, reject) => {throw new Error('报错了');
})
.then(result => result);Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// Error: 报错了
race()

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

const p = Promise.race([p1, p2, p3]);

只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变

率先改变的 Promise 实例的返回值则传递给p的回调函数

const p = Promise.race([fetch('/resource-that-may-take-a-while'),new Promise(function (resolve, reject) {setTimeout(() => reject(new Error('request timeout')), 5000)})
]);p
.then(console.log)
.catch(console.error);
allSettled()

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise实例

只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束

const promises = [fetch('/api-1'),fetch('/api-2'),fetch('/api-3'),
];await Promise.allSettled(promises);
removeLoadingIndicator();
resolve()

将现有对象转为 Promise对象

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

参数可以分成四种情况,分别如下:

1.参数是一个 Promise 实例,promise.resolve将不做任何修改、原封不动地返回这个实例
2.参数是一个thenable对象,promise.resolve会将这个对象转为 Promise对象,然后就立即执行thenable对象的then()方法
3.参数不是具有then()方法的对象,或根本就不是对象,Promise.resolve()会返回一个新的 Promise 对象,状态为resolved
4.没有参数时,直接返回一个resolved状态的 Promise 对象

reject()

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))p.then(null, function (s) {console.log(s)
});
// 出错了
Promise.reject()方法的参数,会原封不动地变成后续方法的参数Promise.reject('出错了')
.catch(e => {console.log(e === '出错了')
})
// true

五、使用场景

将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化

const preloadImage = function (path) {return new Promise(function (resolve, reject) {const image = new Image();image.onload  = resolve;image.onerror = reject;image.src = path;});
};

通过链式操作,将多个渲染数据分别给个then,让其各司其职。或当下个异步请求依赖上个请求结果的时候,我们也能够通过链式操作友好解决问题、

// 各司其职
getInfo().then(res=>{let { bannerList } = res//渲染轮播图console.log(bannerList)return res
}).then(res=>{let { storeList } = res//渲染店铺列表console.log(storeList)return res
}).then(res=>{let { categoryList } = resconsole.log(categoryList)//渲染分类列表return res
})

通过all()实现多个请求合并在一起,汇总所有请求结果,只需设置一个loading即可

function initLoad(){// loading.show() //加载loadingPromise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res=>{console.log(res)loading.hide() //关闭loading}).catch(err=>{console.log(err)loading.hide()//关闭loading})
}
//数据初始化    
initLoad()
通过race可以设置图片请求超时//请求某个图片资源
function requestImg(){var p = new Promise(function(resolve, reject){var img = new Image();img.onload = function(){resolve(img);}//img.src = "https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg"; 正确的img.src = "https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg1";});return p;
}//延时函数,用于给请求计时
function timeout(){var p = new Promise(function(resolve, reject){setTimeout(function(){reject('图片请求超时');}, 5000);});return p;
}Promise
.race([requestImg(), timeout()])
.then(function(results){console.log(results);
})
.catch(function(reason){console.log(reason);
});

在这里插入图片描述


http://www.ppmy.cn/news/1567925.html

相关文章

基于Django的个人博客系统的设计与实现

【Django】基于Django的个人博客系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 系统采用Python作为主要开发语言,结合Django框架构建后端逻辑,并运用J…

SpringAI 搭建智能体(二):搭建客服系统智能体

在现代人工智能应用中,智能体(Agent) 是一个重要的概念,它的核心能力是自主性与灵活性。一个智能体不仅能够理解用户的需求,还能拆解任务、调用工具完成具体操作,并在复杂场景中高效运行。在本篇博客中&…

99.17 金融难点通俗解释:归母净利润

目录 0. 承前1. 简述2. 比喻:小明家的小卖部2.1 第一步:计算收到的所有钱2.2 第二步:减去各种支出2.3 第三步:计算能带回家的钱 3. 生活中的例子3.1 好的经营情况3.2 一般的经营情况3.3 不好的经营情况 4. 小朋友要注意4.1 为什么…

C#通过3E帧SLMP/MC协议读写三菱FX5U/Q系列PLC数据案例

C#通过3E帧SLMP/MC协议读写三菱FX5U/Q系列PLC数据案例,仅做数据读写报文测试。附带自己整理的SLMP/MC通讯协议表。 SLMP以太网读写PLC数据20191206/.vs/WindowsFormsApp7/v15/.suo , 73216 SLMP以太网读写PLC数据20191206/SLMP与MC协议3E帧通讯协议表.xlsx , 10382…

C# OpenCV机器视觉:卡尔曼滤波

在一个阳光有些慵懒的午后,阿强像往常一样窝在他那被各种电子元件和乱糟糟电线堆满的实验室里,百无聊赖地翻看着一本本厚重的技术书籍。突然,一阵急促的敲门声打破了平静,阿强趿拉着拖鞋,嘟囔着跑去开门,只…

Ubuntu 16.04安装Lua

个人博客地址:Ubuntu 16.04安装Lua | 一张假钞的真实世界 在Linux系统上使用以下命令编译安装Lua: curl -R -O http://www.lua.org/ftp/lua-5.3.3.tar.gz tar zxf lua-5.3.3.tar.gz cd lua-5.3.3 make linux test 安装make 编译过程如果提示以下信息…

《Operating System Concepts》阅读笔记:p1-p1

《Operating System Concepts》学习第 1 天,p1-p1 总结,总计 1 页。 一、技术总结 无。 二、英语总结(生词:1) 1.intermediary (1)intermediary: inter-(“between, among”) medius(“middle”) c.intermediary originally referred …

PPT教程:怎样在PPT中嵌入视频或音频文件?

在PPT制作技巧中,嵌入视频或音频文件无疑是一个能够极大增强演示文稿互动性和吸引力的高级技巧。通过巧妙地运用这一功能,你可以将复杂的说明过程简化为生动的视频演示,或者通过背景音乐为演讲增添氛围,从而让观众更加专注并留下深…