这篇文章算是 JavaScript Promises 比较全面的教程,该文介绍了必要的方法,例如 then
,catch
和finally
。 此外,还包括处理更复杂的情况,例如与Promise.all
并行执行Promise
,通过Promise.race
来处理请求超时的情况,Promise 链以及一些最佳实践和常见的陷阱。
Promise 是一个允许我们处理异步操作的对象,它是 es5 早期回调的替代方法。
与回调相比,Promise 具有许多优点,例如:
-
让异步代码更易于阅读。
-
提供组合错误处理。
* 更好的流程控制,可以让异步并行或串行执行。
回调更容易形成深度嵌套的结构(也称为回调地狱
)。 如下所示:
a(() => {
b(() => {
c(() => {
d(() => {
// and so on …
});
});
});
});
如果将这些函数转换为 Promise
,则可以将它们链接起来以生成更可维护的代码。 像这样:
Promise.resolve()
.then(a)
.then(b)
.then©
.then(d)
.catch(console.error);
在上面的示例中,Promise 对象公开了.then
和.catch
方法,我们稍后将探讨这些方法。
我们可以使用 Promise 构造函数将回调转换为 Promise。
Promise 构造函数接受一个回调,带有两个参数resolve
和reject
。
-
Resolve:是在异步操作完成时应调用的回调。
-
Reject:是发生错误时要调用的回调函数。
构造函数立即返回一个对象,即 Promise
实例。 当在 promise 实例中使用.then
方法时,可以在Promise “完成” 时得到通知。 让我们来看一个例子。
Promise 仅仅只是回调?
并不是。承诺不仅仅是回调,但它们确实对.then
和.catch
方法使用了异步回调。 Promise 是回调之上的抽象,我们可以链接多个异步操作并更优雅地处理错误。来看看它的实际效果。
Promise 反面模式(Promises 地狱)
a(() => {
b(() => {
c(() => {
d(() => {
// and so on …
});
});
});
});
不要将上面的回调转成下面的 Promise 形式:
a().then(() => {
return b().then(() => {
return c().then(() => {
return d().then(() =>{
// ⚠️ Please never ever do to this! ⚠️
});
});
});
});
上面的转成,也形成了 Promise 地狱,千万不要这么转。相反,下面这样做会好点:
a()
.then(b)
.then©
.then(d)
超时
你认为以下程序的输出的是什么?
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(‘time is up ⏰’);
}, 1e3);
setTimeout(() => {
reject(‘Oops 🔥’);
}, 2e3);
});
promise
.then(console.log)
.catch(console.error);
是输出:
time is up ⏰
Oops! 🔥
还是输出:
time is up ⏰
是后者,因为当一个Promise resolved
后,它就不能再被rejected
。
一旦你调用一种方法(resolve
或reject
),另一种方法就会失效,因为 promise
处于稳定状态。 让我们探索一个 promise
的所有不同状态。
Promise 可以分为四个状态:
-
⏳ Pending:初始状态,异步操作仍在进行中。
-
✅ Fulfilled:操作成功,它调用
.then
回调,例如.then(onSuccess)
。 -
⛔️ Rejected: 操作失败,它调用
.catch
或.then
的第二个参数(如果有)。 例如.catch(onError)
或.then(..., onError)
。 -
😵 Settled:这是 promise 的最终状态。promise 已经死亡了,没有别的办法可以解决或拒绝了。
.finally
方法被调用。
Promise API 公开了三个主要方法:then
,catch
和finally
。 我们逐一配合事例探讨一下。
大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】。
Promise then
then
方法可以让异步操作成功或失败时得到通知。 它包含两个参数,一个用于成功执行,另一个则在发生错误时使用。
promise.then(onSuccess, onError);
你还可以使用catch
来处理错误:
promise.then(onSuccess).catch(onError);
Promise 链
then
返回一个新的 Promise ,这样就可以将多个Promise 链接在一起。就像下面的例子一样:
Promise.resolve()
.then(() => console.log(‘then#1’))
.then(() => console.log(‘then#2’))
.then(() => console.log(‘then#3’));
Promise.resolve
立即将Promise 视为成功。 因此,以下所有内容都将被调用。 输出将是
then#1
then#2
then#3
Promise catch
Promise .catch
方法将函数作为参数处理错误。 如果没有出错,则永远不会调用catch
方法。
假设我们有以下承诺:1
秒后解析或拒绝并打印出它们的字母。
const a = () => new Promise((resolve) => setTimeout(() => { console.log(‘a’), resolve() }, 1e3));
const b = () => new Promise((resolve) => setTimeout(() => { console.log(‘b’), resolve() }, 1e3));
const c = () => new Promise((resolve, reject) => setTimeout(() => { console.log(‘c’), reject(‘Oops!’) }, 1e3));
const d = () => new Promise((resolve) => setTimeout(() => { console.log(‘d’), resolve() }, 1e3));
请注意,c
使用reject('Oops!')
模拟了拒绝。
Promise.resolve()
.then(a)
.then(b)
.then©
.then(d)
.catch(console.error)
输出如下:
在这种情况下,可以看到a
,b
和c
上的错误消息。
我们可以使用then
函数的第二个参数来处理错误。 但是,请注意,catch
将不再执行。
Promise.resolve()
.then(a)
.then(b)
.then©
.then(d, () => console.log(‘c errored out but no big deal’))
.catch(console.error)
由于我们正在处理 .then(..., onError)
部分的错误,因此未调用catch
。 d
不会被调用。 如果要忽略错误并继续执行Promise链,可以在c
上添加一个catch
。 像这样:
Promise.resolve()
.then(a)
.then(b)
.then(() => c().catch(() => console.log(‘error ignored’)))
.then(d)
.catch(console.error)
当然,这种过早的捕获错误是不太好的,因为容易在调试过程中忽略一些潜在的问题。
Promise finally
finally
方法只在 Promise 状态是 settled
时才会调用。
如果你希望一段代码即使出现错误始终都需要执行,那么可以在.catch
之后使用.then
。
Promise.resolve()
.then(a)
.then(b)
.then©
.then(d)
.catch(console.error)
.then(() => console.log(‘always called’));
或者可以使用.finally
关键字:
Promise.resolve()
.then(a)
.then(b)
.then©
.then(d)
.catch(console.error)
.finally(() => console.log(‘always called’));
我们可以直接使用 Promise
对象中四种静态方法。
-
Promise.all
-
Promise.reject
-
Promise.resolve
-
Promise.race
Promise.resolve 和 Promise.reject
这两个是帮助函数,可以让 Promise 立即解决或拒绝。可以传递一个参数,作为下次 .then
的接收:
Promise.resolve(‘Yay!!!’)
.then(console.log)
.catch(console.error)
上面会输出 Yay!!!
Promise.reject(‘Oops 🔥’)
.then(console.log)
.catch(console.error)
使用 Promise.all
并行执行多个 Promise
通常,Promise 是一个接一个地依次执行的,但是你也可以并行使用它们。
假设是从两个不同的api中轮询数据。如果它们不相关,我们可以使用Promise.all()
同时触发这两个请求。
在此示例中,主要功能是将美元转换为欧元,我们有两个独立的 API 调用。 一种用于BTC/USD
,另一种用于获得EUR/USD
。 如你所料,两个 API 调用都可以并行调用。 但是,我们需要一种方法来知道何时同时完成最终价格的计算。 我们可以使用Promise.all
,它通常在启动多个异步任务并发运行并为其结果创建承诺之后使用,以便人们可以等待所有任务完成。
const axios = require(‘axios’);
const bitcoinPromise = axios.get(‘https://api.coinpaprika.com/v1/coins/btc-bitcoin/markets’);
const dollarPromise = axios.get(‘https://api.exchangeratesapi.io/latest?base=USD’);
const currency = ‘EUR’;
// Get the price of bitcoins on
Promise.all([bitcoinPromise, dollarPromise])
.then(([bitcoinMarkets, dollarExchanges]) => {
const byCoinbaseBtc = d => d.exchange_id === ‘coinbase-pro’ && d.pair === ‘BTC/USD’;
const coinbaseBtc = bitcoinMarkets.data.find(byCoinbaseBtc)
const coinbaseBtcInUsd = coinbaseBtc.quotes.USD.price;
const rate = dollarExchanges.data.rates[currency];
return rate * coinbaseBtcInUsd;
})
.then(price => console.log(The Bitcoin in ${currency} is ${price.toLocaleString()}
))
.catch(console.log);
如你所见,Promise.all
接受了一系列的 Promises。 当两个请求的请求都完成后,我们就可以计算价格了。
我们再举一个例子:
const a = () => new Promise((resolve) => setTimeout(() => resolve(‘a’), 2000));
const b = () => new Promise((resolve) => setTimeout(() => resolve(‘b’), 1000));
const c = () => new Promise((resolve) => setTimeout(() => resolve(‘c’), 1000));
const d = () => new Promise((resolve) => setTimeout(() => resolve(‘d’), 1000));
console.time(‘promise.all’);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后的最后
面试题千万不要死记,一定要自己理解,用自己的方式表达出来,在这里预祝各位成功拿下自己心仪的offer。
需要完整面试题的朋友可以点击蓝色字体获取
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-8tFHZir1-1713142059017)]
[外链图片转存中…(img-7TzjEMkc-1713142059018)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
[外链图片转存中…(img-pCHOtm8L-1713142059018)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后的最后
面试题千万不要死记,一定要自己理解,用自己的方式表达出来,在这里预祝各位成功拿下自己心仪的offer。
需要完整面试题的朋友可以点击蓝色字体获取
[外链图片转存中…(img-6R3c4W55-1713142059018)]
[外链图片转存中…(img-xjkOusyR-1713142059018)]
[外链图片转存中…(img-eKdYoWAJ-1713142059019)]
[外链图片转存中…(img-jpmFoXiQ-1713142059019)]