当前位置:网站首页>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的回调函数。
边栏推荐
猜你喜欢
UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xd6 in position 120: invalid continuation byte
立方度量(Cubic Metric)
未知点云结构文件转换需求
Debian防火墙的开关以及状态
Axure9基本交互操作(一)
Develop your own text recognition application with Tesseract
In which industries is the PMP certificate useful?
开发deepstram的自定义插件,使用gst-dseaxmple插件进行扩充,实现deepstream图像输出前的预处理,实现图像自定义绘制图(精四)
Flutter 实现背景图片毛玻璃效果
unity2D横版游戏教程9-对话框dialog
随机推荐
Webmine Webpage Mining Trojan Analysis and Disposal
[Linear Algebra 02] 2 interpretations of AX=b and 5 perspectives of matrix multiplication
《剑指offer》刷题分类
EasyGBS接入最新版海康摄像头后无法传递告警信息该如何解决?
OC-协议
ROS packages visualization
mysql基础
软件测试外包公司怎么样?有什么好处和坏处?为什么没人去?
Unknown point cloud structure file conversion requirements
27. Dimensionality reduction
How to solve the problem that the alarm information cannot be transmitted after EasyGBS is connected to the latest version of Hikvision camera?
Chapter7 : Network-Driven Drug Discovery
Cocoa Application-test
ROS播包可视化
论文解读(PPNP)《Predict then Propagate: Graph Neural Networks meet Personalized PageRank》
零基础都能拿捏的七夕浪漫代码,快去表白或去制造惊喜吧
The upgrade and transformation plan of the fortress machine for medium and large commercial banks!Must see!
3D激光SLAM:LeGO-LOAM---两步优化的帧间里程计及代码分析
[QT] Implementation of callback function
【ubuntu20.04安装MySQL以及MySQL-workbench可视化工具】