通俗易懂之Promise!

通俗易懂之Promise!

The局外人 Lv Max

都知道javaScript代码是单线程执行的,就是这个原因,导致了JavaScript中所有的网络操作,浏览器事件,都必须满足异步执行的要求,此时Promise就诞生了。简单来说Promise是一种处理异步请求的解决方案。

初始Promise

Promise三种状态

Promise 是一个构造函数,需要使用new关键字生成实例

1
2
3
4
let p = new Promise(() => {

})
console.log(p);

这里写了一个简单的Promise,输出如下结果:
image1.png
可以看出在Promise未指定任何状态时,返回的状态为pending(等待状态),还有两段代码:

1
2
3
4
let p = new Promise((resolve, reject) => {
resolve('ok')
})
console.log(p);
1
2
3
4
let p = new Promise((resolve, reject) => {
reject('err')
})
console.log(p);

两段代码的结果分别为:
image2.pngimage3.png
不难看出,Promise的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:

  • resolve :异步操作执行成功后的回调函数,指定了成功的状态为fulfiled(成功状态),且成功状态的值为你设置的ok
  • reject:异步操作执行失败后的回调函数,指定了失败的状态为rejected(失败状态),且失败状态的值为你设置的err

注:失败的回调这里控制台有报错信息,这里不是我们的代码错误,是Promise 的失败回调函数抛出了一个错误。

Promise状态不能被改变

有这么一段代码:

1
2
3
4
5
let p = new Promise((resolve, reject) => {
resolve('ok')
reject('err')
})
console.log(p);

此时结果:
image2.png
由此得出Promise状态一旦改变就不会再变了,创造Promise实例后它会立即执行

.then

.then的链式调用

有这么一个需求:

请求三次数据,请求第一次的参数为params=321,而后两次参数分别为前一次返回的结果,是你你会如何实现?

普通方式解决如下:

1
2
3
4
5
6
7
8
9
10
getData('/api/a/1?params=321',(res1) => {
console.log(res1);
getData(`/api/b/2?params=${res1.data.params}`, (res2) => {
console.log(res2);
getData(`/api/c/3?params=${res2.data.params}`, (res3) => {
console.log(res3);
})
})
})

以上这种方式就是传说中的回调地狱–>回调里面套回调。
此时我们使用Promise来优化上述代码:

1
2
3
4
5
6
7
8
9
10
getData('/api/a/1?params=321').then((res1)=>{
console.log(res1)
return getData(`/api/b/2?params=${res1.data.params}`
}).then((res2)=>{
console.log(res2)
return getData(`/api/c/3?params=${res2.data.params}`
}).then((res3)=>{
//得到最终结果
console.log(res3)
})

被优化后的代码是通过.then解决后的结果,也是成功的解决了回调地狱的问题。
又有两段代码:

1
2
3
4
5
6
7
8
let p = new Promise((resolve, reject) => {
resolve('ok')
})

let result = p.then((res) => {
return 'ok'
})
console.log(result);

得到结果为
image2.png

1
2
3
4
5
6
7
8
9
10
let p = new Promise((resolve, reject) => {
resolve('ok')
})

let result = p.then((res) => {
return new Promise((resolve, reject) => {
reject('err')
})
})
console.log(result);

结果为:
image3.png
因为链式调用的结果上述代码中的p.then也是一个Promise,由此得出:
如果Promise的then方法的成功或失败返回是非Promise,那么then方法返回的Promise实例就为成功的,值为return的那个非Promise值,如果返回的是Promise,那么then方法返回的Promise实例就取决于上个Promise的返回结果

.then的两个参数

.then可以接收两个参数,并且两个参数都是回调函数,有这么一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let p = new Promise((resolve, reject) => {
let name = '张学友'
if (name === '刘德华') {
resolve('这是刘德华')
} else if (name === '张学友') {
resolve('这是张学友')
} else {
reject('啥也不是')
}
})
p.then((res) => {
console.log('成功',res);
}, (err) => {
console.log('失败', err);
})

上述代码当你随意切换name的值会发现,你使用resolve指定为成功的结果时,then方法就会执行第一个成功回调,若你使用reject指定为失败的结果时,then方法就会执行第二个失败回调。

.catch

首先我来演示一个错误:

1
2
3
4
5
6
7
8
9
let p = new Promise((resolve, reject) => {
resolve('ok')
})
p.then((res) => {
console.log(res);
console.log(a);
},(err)=>{
console.log(err);
})

如上述代码所示,我定义了一个为定义的变量a,运行结果如下图:
image4.png
可以看出报错终止了代码的运行,,错误回调并没捕获错误的结果。此时我换成catch:

1
2
3
4
5
6
7
8
9
let p = new Promise((resolve, reject) => {
resolve('ok')
})
p.then((res) => {
console.log(res);
console.log(a);
}).catch((err)=>{
console.log(err);
})

结果为:
image5.png
也就是说进入catch中时,把错误原因传到参数中,即便有错误代码也不会报错了,与try/catch相似。

Promise.all()

Promise下的all方法接受一个由多个Promise组成的数组,所有Promise结果成功,才返回成功的Promise回调,上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function getLunbo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('请求轮播图成功')
}, 1000)
})
}
function getTab() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('请求tab栏成功成功')
}, 2200)
})
}
function getLayOut() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('请求侧边栏成功')
}, 3000)
})
}
function getPic() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('请求图片成功')
}, 1200)
})
}
let a = getLunbo()
let b = getTab()
let c = getLayOut()
let d = getPic()
let all = Promise.all([a, b, c, d]).then((value) => {
console.log(value);
console.log('请求所有数据成功');
})

结果如下
image6.png
如果全部成功,就返回所有成功结果组成的数组,如果其中一个Promise返回失败的结果则:
image7.png

Promise.any()

Promise下的any方法也接受一个由多个Promise组成的数组,一个Promise返回结果为成功,整体就返回成功的promise,所有的都失败才返回失败的promise。

1
2
3
4
5
6
7
8
9
10
11
12
13
let x1 = new Promise((resolve, reject) => {
resolve('ok')
})
let x2 = new Promise((resolve, reject) => {
reject('err')
})
let x3 = new Promise((resolve, reject) => {
reject('err1')
})
let any = Promise.any([x1, x2, x3]).then((value) => {
console.log(value);
})
console.log(any);

结果为
image8.png
如果都是失败的Promise则:
image9.png

Promise.race()

race 赛跑的意思,以第一个有结果的promise为主,成功即为成功,失败即为失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(2);
}, 3000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3);
}, 5000)
})
const result = Promise.race([p3, p1, p2]);
console.log(result);

上述代码执行最快的是第一个,所以Promise.race([p3, p1, p2]);的结果取决于第一个,第一个为pending,result的结果就是pending,为成功就成功,为失败就失败。

Promise.finlly()

1
2
3
4
5
6
7
8
9
10
11
const p1 = new Promise((resolve, reject) => {
//resolve('ok');
reject('error');
});
p1.then(value => {
console.log(value);
}).catch(reason => {
console.log(reason);
}).finally(() => {
console.log('最终我被执行了...');
})

不难看出,不管成功与否,失败与否都会执行finally(),且finally回调函数不接收参数

Promise.allSettled()

当所有的异步操作都有结果时,包装实例才结束,返回**成功的Promise,**目前我未曾用过这个方法,后续我将补充

1
2
3
4
5
6
7
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

let result = Promise.allSettled(promises).then((results) => results.forEach((result) => console.log(result.status)));
//返回成功的Promise
console.log(result);

上述代码摘自MDN

Promise.resolve()

当Promise.resolve()中传入了非Promise,那么包裹对象返回成功的Promise,成功的结果为非Promise的,如果传入的是Promise,那么包裹对象返回的结果为Promise返回的结果

Promise.reject()

直接指定为失败状态的Promise,返回一个带有拒绝原因reason参数的Promise对象。

anync与await

await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待等待成功的回调。
await必须写在async函数中,但是async函数中可以没有await
如果await的promise失败了,就会抛出异常,需要通过try…catch捕获处理

1
2
3
4
5
6
7
8
9
async function getData () {
try{
//成功
let res1 = await getDataList('/api/a/1?params=123');
}catch(err){
//失败
console.log(err.message)
}
}

上述代码anync与await的基本使用,后续还会补充。

最后

关于Promise的总结

总结

**
Promise可以解决代码的回调地狱(回调里面套回调),从而简化代码
Promise可以解决异步的问题,但本身不能说Promise是异步的
anync与await是开发主流,可通过try…catch捕获异常
**


希望大家有所收获!!!!!!!!!!!!!!!!!!!!!!!!

  • 标题: 通俗易懂之Promise!
  • 作者: The局外人
  • 创建于 : 2024-05-14 11:43:01
  • 更新于 : 2023-09-05 13:02:38
  • 链接: https://dragon-xjy.gitee.io/2024/05/14/通俗易懂之Promise/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
 评论