当前位置:网站首页>promise详解
promise详解
2022-08-04 21:58:00 【F班的小夏同学】
promise是异步编程的一种解决方案,比起传统的解决方案——回调函数和事件——更加的强大。
在项目中,我们经常会遇到异步的问题,在之前,我们通常使用回调函数来解决异步编程,但是在一些比较复杂的情况下,我们经常会陷入回调地狱。promise能够很好的帮助我们解决这个问题。
早就听说过Promise的大名,但是promise到底是个什么东西呢?是个类?对象?数组?函数?来,先给大家拉出来溜溜。
这里我们可以看到,原来这个promise是一个构造函数,并且在它自己的身上有all
、reject
、resolve
这几个特别眼熟的方法,在它的原型上还有then
、finally
、catch
等方法,也就是说,我们在用Promise new实例化一个对象时,这个对象一定有then
、finally
、catch
这些方法。
【Promise的生命周期】
promise对象有三个状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。
每个Promise都会经历一个短暂的生命周期:先是处于进行中(pending)的状态,此时操作尚未完成,操作结束后,Promise可能会进入到以下两个状态中的其中一个。
假如异步操作成功完成,进入(fulfilled)状态;假如异步操作没有成功,进入(rejected)状态。
注意:promise有一个特点,就是一旦状态改变,就不会再改变,Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。这个时候,再给promise添加回调函数就会立即调用,这一点就和事件回调很不一样,事件回调是在错过事件之后再去监听是无法得到结果的。
【应用】
我们先来试一下,改写一个普通方法变为一个promise实例,更加方便大家的理解。
原方法如下:
export function getInfo () {
return '返回一个字符串'
}
我们在调用的时候:
// 原方法调用如下
const str = getInfo()
console.log(str)
修改成promise实例之后:
export function getInfo () {
return new Promise((resolve, reject) => {
const str = '返回一个字符串'
if (str === undefined) {
//抛出异常
reject(new Error('异常:空字符串'))
}
//继续执行
resolve(str)
})
}
// 方法调用如下
getInfo.then(str => {
console.log(str)
})
【基本用法】
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
我们可以看到promise接收了一个函数作为参数,这个函数有两个参数,分别是resolve和reject,他们是js引擎自己提供的,不用我们来配置。
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
promise实例生成之后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then方法接受了两个参数,第一个参数是当promise被resolve之后执行的方法,第二个参数是promise被rejected之后执行的方法。这两个参数都是可选的,不一定都要写,他们两个都可以接受promise传递出来的参数。
【promise在创建之后就会立即执行】(这也是为什么我们经常将promise作为一个函数的返回值来使用)
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hahahahah');
// Promise
// Hahahahah
// resolved
上面的代码中,promise对象创建之后立即执行,所以先输出promise,但是resolve指定的回调函数会在当前脚本的同步任务全部执行完之后再执行,所以会先输出Hahahahah,最后输出resolved。
【关于promise传递给回调函数的参数】
我们知道,在promise执行完之后可以给回调函数传递参数,如果是rejected,一般会给回调函数传递一个错误的实例;如果是resolve,除了传递一般的普通的正常值以外,有时候我们还能看到传递另一个promise实例参数的情况。
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1);
})
上面代码中,p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。
注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。
给大家看一个实例:
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
上面代码中,p1是一个 Promise,3 秒之后变为rejected。p2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。
【常用方法】
【Promise.prototype.then()】
从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
这样采用then的链式用法,就解决了回调地狱的问题。
【Promise.prototype.catch()】
Promise.prototype.catch()用于在promise返回错误时的回调函数
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
这个时候我们有问题了,我们的then不是也有这样的作用吗?then()的第二个参数就是promise执行失败的时候的回调函数啊。
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) {
//cb
// success
})
.catch(function(err) {
// error
});
第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用catch()方法,而不使用then()方法的第二个参数。
【Promise.prototype.finally()】
promise
.then(result => {
···})
.catch(error => {
···})
.finally(() => {
···});
【Promise.all()】
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
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.race() 】
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
边栏推荐
- Ramnit感染型病毒分析与处置
- Chapter7 : Network-Driven Drug Discovery
- In action: 10 ways to implement delayed tasks, with code!
- 七夕特制:《牛郎会织女》
- Altium Designer 19.1.18 - 保护锁定的对象
- ctfshow终极考核web654
- 【C - 基本概念】
- Exploration and Practice of Database Governance
- In which industries is the PMP certificate useful?
- Autowired autowiring
猜你喜欢
2022精选最新金融银行面试真题——附带答案
【社媒营销】WhatsApp Business API:您需要知道的一切
年薪40W测试工程师成长之路,你在哪个阶段?
Ramnit感染型病毒分析与处置
硬件开发定制全流程解析
开发deepstram的自定义插件,使用gst-dseaxmple插件进行扩充,实现deepstream图像输出前的预处理,实现图像自定义绘制图(精四)
立即升级!WPS Office 出现 0day 高危安全漏洞:可完全接管系统,官方推出紧急更新
Chapter7 : Network-Driven Drug Discovery
【QT】回调函数的实现
【CC3200AI 实验教程 1】疯壳·AI语音人脸识别(会议记录仪/人脸打卡机)-开发环境搭建
随机推荐
input事件中文触发多次问题研究php DEBUG
LeetCode: 406. 根据身高重建队列
Win11如何开启Telnet客户端?
unity2D横版游戏教程9-对话框dialog
多个平台显示IP属地,必须大力推行互联网实名制
ROS packages visualization
MySQL查询为啥慢了?
国际项目管理师PMP证书,值得考嘛?
Rt-thread [三] link.lds链接脚本详解
Why is MySQL query slow?
PowerBI真经连续剧
如何将二叉搜索树转化为一个有序的双向链表(原树上修改)
2022强网杯web(部分)
The upgrade and transformation plan of the fortress machine for medium and large commercial banks!Must see!
OC-归档(序列化)(了解的不多 没细看)
如何一键重装Win11系统 一键重装系统方法
限制tensorflow使用Cpu核数
今天是七夕,来看看程序员的土味情话。
基于 Milvus 和 ResNet50 的图像搜索(部署及应用)
How to solve the problem that the alarm information cannot be transmitted after EasyGBS is connected to the latest version of Hikvision camera?