如果你是一个JavaScript初学者,你可能正在努力理解什么是真正的承诺。
我最近在Twitter上发表了这个话题,并被大家的反应吓了一跳。所以我决定将其扩展为一个关于JavaScript承诺的入门教程。
我读过很多关于承诺的文章,问题是很多指南都没有以一种亲和的方式解释它们。人们不理解JavaScript中的承诺是什么,是因为他们没有真正了解它是什么,以及它是如何以简单和可联系的方式表现出来的。
所以在这篇文章中,我将告诉你一个小故事,解释什么是承诺,以及它们到底是如何工作的。我还会通过一些例子告诉你如何在你的JavaScript中使用承诺。
什么是JavaScript中的承诺?
想象一下,你正在为你公司的一个职位面试求职者。
一个年轻人急匆匆地来参加面试。当他的面试即将开始时,他意识到他忘记了他的简历。
很遗憾,对吗?
不过,他并没有被吓倒。幸运的是,他有一个室友,当时还在家里。
他迅速通过电话给他的室友打电话,请他帮忙。他恳求他的室友帮助找到他的履历表。他的室友答应一有消息就回短信。
假设简历最终被找到,他就可以回短信了。
"成功,我找到了你的简历!"
但是,如果他没有找到,他应该发回一条失败信息,说明他找不到简历的原因。例如,他可以给正在面试的朋友发这样的信息。
"对不起,我找不到你的简历,因为你的保险箱的钥匙不见了。"
与此同时,面试继续按计划进行,面试官坚守着找到简历的承诺,而不是真正的简历。这时,面试官将简历交付的状态设置为待定。
被面试者回答了所有被问到的问题。但最终,他的就业仍然取决于他简历的最终状态。
他的室友终于回了短信。正如我们之前所讨论的,如果他没有找到简历,他将与你分享这一失败,以及他没有找到简历的原因。
当这种情况发生时,面试将结束,面试者将被拒绝。
另一方面,如果室友找到了简历,他就会高兴地告诉他的朋友他成功了,他就会继续下去,实现他获得工作的希望。
那么,这如何转化为JS代码呢?
室友承诺找到履历表并发回短信,这与我们在JavaScript中定义承诺的方式是一样的。这段代码并不直接或立即返回一个值。相反,它返回一个承诺,即它最终会在以后的时间里提供这个值。
JavaScript中的承诺是异步的,意味着它需要时间来解决或完成。就像搜索求职者的简历需要时间来完成。
出于这个原因,面试官决定不坐以待毙,所以他们根据简历交付的承诺开始对候选人进行面试。我们用返回简历的承诺来代替实际的简历。
JS引擎也不会无所事事地等待 - 它开始执行代码的其他部分,等待承诺的返回值。
消息文本包含简历搜索的状态信息。对于JavaScript Promise来说,这也被称为返回值。
如果消息是 "成功",我们将继续签署候选人,并授予他这个职位。如果失败,我们就着手拒绝他的申请。
对于JavaScript的承诺,我们通过 使用回调函数(承诺处理程序)来实现。这些函数被定义在一个嵌套的then()
方法中。
为了指定调用哪些回调,你可以使用以下两个函数:
resolve(value)
:这表明异步任务是成功的。这将调用then()
处理程序中的履行回调。reject(error)
:这表明在尝试运行异步任务时出现了错误。这将调用then()
处理程序中的rejection回调。
如果承诺是成功的,将调用履行回调。如果承诺被拒绝,将调用拒绝回调。
一个承诺只是一个尚未完成的异步任务的占位符。当你在脚本中定义一个promise对象时,它不是立即返回一个值,而是返回一个promise。
如何在JavaScript中编写一个诺言
你可以通过调用Promise
类并构造一个像这样的对象来在你的JavaScript中定义一个承诺:
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('this is the eventual value the promise will return');
}, 300);
});
console.log(myPromise);
代码示例一
在控制台中运行这个将返回一个Promise
对象:
不过,构造一个对象并不是你定义承诺的唯一方法。你也可以使用内置的Promise
API来实现同样的事情。
const anotherPromise = Promise.resolve("this is the eventual value the promise will return")
console.log(anotherPromise);
代码示例二
第一个代码样本中的承诺将等待3秒钟,然后用this is the eventual...
消息来履行承诺,而第二个代码样本中的承诺将立即用同样的消息来履行。
JavaScript中被拒绝的承诺
Promise也可以被拒绝。大多数情况下,拒绝的发生是因为JS在运行异步代码时遇到了某种错误。在这种情况下,它会调用reject()
函数来代替。
下面是一个简单的、伪造的例子,说明承诺如何被拒绝:
const myPromise = new Promise((resolve, reject) => {
let a = false;
setTimeout(() => {
return (a) ? resolve('a is found!'): reject('sorry, no a');
}, 300);
});
代码示例三
你能想到这个承诺被拒绝的原因吗?如果你说 "因为a
不是假的",那么恭喜你!在第三个代码示例中的承诺将被拒绝。
第三个代码示例中的承诺在超时三秒后将解析为拒绝,因为(a)?
语句解析为false,这将触发reject
。
如何用链式承诺then()
当承诺最终返回一个值时,你通常会想对该返回值做些什么。
例如,如果你在做一个网络请求,你可能想访问这个值并在页面上显示给用户。
你可以定义两个回调函数,当一个承诺被实现或被拒绝时,你想被调用。这些函数被定义在一个嵌套的then()
方法里面:
const anotherPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('this is the eventual value the promise will return');
}, 300);
});
// CONTINUATION
anotherPromise
.then(value => { console.log(value) })
代码示例四
运行这段代码将在三秒后在控制台显示履行信息:
请注意,你可以根据你的需要嵌套许多承诺。每一步都将在前一步之后执行,吸收前一步的返回值:
const anotherPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('this is the eventual value the promise will return');
}, 300);
});
anotherPromise
.then(fulfillFn, rejectFn)
.then(fulfilFn, rejectFn)
.then(value => { console.log(value) })
代码示例五
但我们漏掉了一些重要的东西。
永远记住,一个then()
方法必须同时接受履行处理程序和拒绝处理程序。这样,如果承诺被履行,就会调用第一个处理程序,如果承诺被错误拒绝,就会调用第二个处理程序。
代码样本四和五中的承诺不包括第二个处理程序。所以,假设遇到了错误,就没有拒绝处理程序来处理这个错误。
如果你只打算在then()
中定义一个回调函数(又称履行处理程序),那么你将需要在承诺链的底部嵌套一个catch()
方法来捕捉任何可能的错误。
如何在JS中使用catch()
方法
只要在承诺链的任何一点上遇到错误,catch()
方法就会被调用:
const myPromise = new Promise((resolve, reject) => {
let a = false;
setTimeout(() => {
return (a) ? resolve('a is found!'): reject('sorry, no a');
}, 300);
});
myPromise
.then(value => { console.log(value) })
.catch(err => { console.log(err) });
代码示例六
由于myPromise
最终会解析为拒绝,嵌套的then()
中定义的函数将被忽略。相反,catch()
中的错误处理程序将被运行,它应该向控制台记录以下错误信息:
结束语
JavaScript承诺是一个非常强大的功能,可以帮助你在JavaScript中运行异步代码。在大多数(如果不是全部)使用JavaScript的职位的面试中,你的面试官可能会问一个关于承诺的问题。
在这篇文章中,我用简单的语言解释了什么是承诺,并通过一些代码实例展示了其基本的实际用法。
我希望你能从这篇文章中得到一些有用的东西。如果你喜欢这样的编程相关教程,你应该看看 我的博客。我定期在那里发表关于软件开发的文章。
谢谢你的阅读,再见。