了解JavaScript中的承诺

了解JavaScript中的承诺
我向您承诺,到本文结束时,您将更好地了解JavaScript Promises。

我与JavaScript有一种“爱与恨”的关系。 但是尽管如此,JavaScript一直吸引着我。 在过去的10年中从事Java和PHP的工作后,JavaScript看起来非常不同,但很有趣。 我没有花足够的时间在JavaScript上,并且一直试图弥补这一点。

承诺是我遇到的第一个有趣的话题。 我一次又一次听到人们说Promises使您从Callback地狱中解救出来。 尽管这可能是令人愉快的副作用,但Promises还有很多,而这是我到目前为止能够弄清的。 这将是一篇长篇文章,如果您想突出显示某些部分,可以使用我们的扩展名http://bit.ly/highlights-extension

背景

当您第一次开始使用JavaScript时,可能会有些沮丧。 您会听到有人说JavaScript是同步编程语言,而其他人则说它是异步的。 您会听到阻塞代码,非阻塞代码,事件驱动的设计模式,事件生命周期,函数堆栈,事件队列,冒泡,polyfill,babel,angular,reactJS,vue JS以及大量其他工具和库。 不用。 您不是第一个。 也有一个术语。 这称为JavaScript疲劳 该推文很好地捕捉了它。

了解JavaScript中的承诺

如果您想了解有关JavaScript疲劳的更多详细信息,请查看以下文章。 这篇帖子在Hackernoon上获得42k拍手是有原因的:)

JavaScript是一种同步编程语言。 但是由于有了回调函数,我们可以使其像异步编程语言一样起作用。

外行的承诺

JavaScript中的承诺与您在现实生活中所做的承诺非常相似。 因此,首先让我们看看现实生活中的承诺。

字典中promise的定义如下

Promise :名词:确保某人会做某事或某件事会发生。

那么,当有人向你许诺时会发生什么呢?

  1. 一个承诺给您保证一定会做的事情。 他们(做出承诺)将自己完成还是由他人完成,这并不重要。 他们给您保证,您可以基于此计划。
  2. 一个诺言既可以兑现也可以兑现。
  3. 当信守诺言时,您会期望从诺言中得到一些收益。 您可以将promise的输出用于您的进一步操作或计划。
  4. 当诺言被破坏时,您想知道为什么诺言的人不能跟上他的承诺。 一旦知道原因并确认诺言已被兑现,您就可以计划下一步该怎么做或如何处理它。
  5. 在作出承诺时,我们所拥有的只是保证。 我们将无法立即采取行动。 承诺兑现 (因此我们有预期的结果)或违约 (我们知道原因,因此我们可以计划应急计划)时,我们可以决定并制定需要做什么。
  6. 您有可能根本没有听到作出承诺的人的回音。 在这种情况下,您希望保留时间阈值。 假设做出承诺的人在10天内没有回到我身边,我会认为他遇到了一些问题,不会遵守诺言。 因此,即使对方在15天后回到您身边,也不再重要,因为您已经制定了替代计划。

JavaScript中的承诺

根据经验,对于JavaScript,我总是从MDN Web Docs阅读文档。 在所有资源中,我认为它们提供了最简洁的细节。 从MDSN Web Docs中阅读了Promises页面,并玩弄了一些代码以了解它。

理解诺言有两个部分。 承诺的创建和承诺的 处理 尽管我们的大多数代码通常都可以满足其他库创建的诺言的处理,但是完全理解将对我们有一定帮助。 一旦您进入初学者阶段,对“创造承诺”的理解同样重要。

创造承诺

让我们看看创建新承诺的签名。

new Promise( /* executor */ function(resolve, reject) { ... } );

构造函数接受一个称为执行程序的函数。 executor函数接受两个参数resolvereject ,这两个参数又是函数。 通常使用Promise来简化异步操作或阻塞代码的处理,例如文件操作,API调用,DB调用,IO调用等。这些异步操作的启动在executor函数中进行。 如果异步操作成功,则由promise的创建者调用resolve函数来返回预期结果。 同样,如果出现一些意外错误,则通过调用reject函数将原因继续传递。

现在我们知道了如何创建承诺。 为了我们的理解,让我们创建一个简单的承诺。

var keepsHisWord;
keepsHisWord = true;
promise1 = new Promise(function(resolve, reject) {
if (keepsHisWord) {
resolve("The man likes to keep his word");
} else {
reject("The man doesnt want to keep his word");
}
});
console.log(promise1);
了解JavaScript中的承诺
每一个承诺都有状态和价值

由于此诺言已得到立即解决,因此我们将无法检查诺言的初始状态。 因此,让我们创建一个新的承诺,这将需要一些时间来解决。 最简单的方法是使用setTimeOut函数。

promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve({
message: "The man likes to keep his word",
code: "aManKeepsHisWord"
});
}, 10 * 1000);
});
console.log(promise2);

上面的代码只是创建了一个承诺,它将在10秒后无条件解决。 因此,我们可以检查诺言的状态,直到兑现为止。

了解JavaScript中的承诺
承诺状态,直到解决或被拒绝

一旦十秒过​​去,诺言就解决了。 PromiseStatusPromiseValue更新。 如您所见,我们更新了resolve函数,以便我们可以传递JSON Object而不是简单的字符串。 这只是为了表明我们也可以在resolve函数中传递其他值。

了解JavaScript中的承诺
在10秒后以JSON对象作为返回值解析的承诺

现在让我们来看一个将被拒绝的诺言。 让我们为此稍微修改promise 1。

keepsHisWord = false;
promise3 = new Promise(function(resolve, reject) {
if (keepsHisWord) {
resolve("The man likes to keep his word");
} else {
reject("The man doesn't want to keep his word");
}
});
console.log(promise3);

由于这将创建未经处理的拒绝,因此Chrome浏览器将显示错误。 您现在可以忽略它。 我们稍后再讲。

了解JavaScript中的承诺
拒绝承诺

如我们所见, PromiseStatus可以具有三个不同的值。 pending resolved或已rejected pending创建承诺时, PromiseStatus将处于pending状态,并且在承诺被resolved或被rejected.之前, PromiseValue状态为undefined rejected. 当一个承诺处于已resolved或被rejected状态时,一个承诺被称为已settled. 因此,承诺通常会从待处理状态过渡到已解决状态。

现在我们知道了如何创建承诺,现在我们可以看看如何使用或处理承诺。 这将与理解Promise对象并驾齐驱。

了解承诺对象

根据MDN文档

Promise对象表示异步操作的最终完成(或失败)及其结果值。

Promise对象具有静态方法和prototype methods在静态方法Promise对象可以被独立地应用,而prototype methods上的实例被应用于需要Promise对象。 记住正常的方法和原型都返回Promise ,这使事情的理解变得容易得多。

原型方法

让我们首先从prototype methods开始。共有三种。 重申一下,所有这些方法都可以应用于Promise对象的实例,并且所有这些方法依次返回promise。 以下所有方法为诺言的不同状态转换分配处理程序。 正如我们之前看到的,创建Promise时,它处于pending状态。 当一个承诺是基于他们是否解决一个或多个以下三种方法将运行fulfilledrejected

Promise.prototype.catch(onRejected)

Promise.prototype.then(onFulfilled, onRejected)

Promise.prototype.finally(onFinally)

下面的图像示出了用于流.then.catch方法。 由于它们返回了Promise因此可以再次链接它们,这也显示在图像中。 如果.finally被宣布为一个承诺那么无论什么时候承诺是它将被执行settled其是否满足或拒绝无关。 正如康斯坦丁·鲁达(Konstantin Rouda)所指出的,对“最终”的支持有限,因此请在使用前进行检查。

了解JavaScript中的承诺
来自: https : //mdn.mozillademos.org/files/15911/promises.png

这是一个小故事。 您是一个正在上学的孩子,您问妈妈电话。 她说:“我将在本月底购买一部电话。”

让我们看看如果在月底执行了Promise,它将在JavaScript中的外观如何。

var momsPromise = new Promise(function(resolve, reject) {
momsSavings = 20000;
priceOfPhone = 60000;
if (momsSavings > priceOfPhone) {
resolve({
brand: "iphone",
model: "6s"
});
} else {
reject("We donot have enough savings. Let us save some more money.");
}
});
momsPromise.then(function(value) {
console.log("Hurray I got this phone as a gift ", JSON.stringify(value));
});
momsPromise.catch(function(reason) {
console.log("Mom coudn't buy me the phone because ", reason);
});
momsPromise.finally(function() {
console.log(
"Irrespecitve of whether my mom can buy me a phone or not, I still love her"
);
});

输出将是。

了解JavaScript中的承诺
妈妈没有兑现诺言。

如果我们将妈妈momsSavings的价值momsSavings为200000,则妈妈将能够为儿子送礼。 在这种情况下,输出将是

了解JavaScript中的承诺
妈妈信守诺言

让我们戴上消耗这个图书馆的人的帽子。 我们正在模拟输出和性质,以便我们可以研究如何使用然后有效捕获。

因为.then可以分配两个onFulfilled, onRejected handlers ,而不是单独的写.then.catch我们可以做同样的用.then它看起来会像下面。

momsPromise.then(
function(value) {
console.log("Hurray I got this phone as a gift ", JSON.stringify(value));
},
function(reason) {
console.log("Mom coudn't buy me the phone because ", reason);
}
);

但是为了代码的可读性,我认为最好将它们分开。

为了确保我们可以在常规浏览器或特定于chrome的浏览器中运行所有这些示例,请确保我们的代码示例中没有外部依赖项。 为了更好地理解其他主题,让我们创建一个函数,该函数将返回将被随机解决或拒绝的promise,以便我们可以测试各种情况。 为了理解异步函数的概念,让我们在函数中引入一个随机延迟。 由于我们将需要随机数,因此让我们首先创建一个随机函数,该函数将返回x和y之间的随机数。

function getRandomNumber(start = 1, end = 10) {
//works when both start,end are >=1 and end > start
return parseInt(Math.random() * end) % (end-start+1) + start;
}

让我们创建一个函数,该函数将为我们返回承诺。 让我们把我们的功能promiseTRRARNOSG这是一个别名promiseThatResolvesRandomlyAfterRandomNumnberOfSecondsGenerator 此函数将创建一个承诺,该承诺将在2到10之间的随机秒数后解析或拒绝。要对拒绝和解析进行随机化,我们将在1到10之间创建一个随机数。如果生成的随机数大于5,我们将解析答应,否则我们将拒绝。

function getRandomNumber(start = 1, end = 10) {
//works when both start and end are >=1
return (parseInt(Math.random() * end) % (end - start + 1)) + start;
}
promiseThatResolvesRandomlyAfterRandomNumnberOfSecondsGenerator
return new Promise(function(resolve, reject) {
let randomNumberOfSeconds = getRandomNumber(2, 10);
setTimeout(function() {
let randomiseResolving = getRandomNumber(1, 10);
if (randomiseResolving > 5) {
resolve({
randomNumberOfSeconds: randomNumberOfSeconds,
randomiseResolving: randomiseResolving
});
} else {
reject({
randomNumberOfSeconds: randomNumberOfSeconds,
randomiseResolving: randomiseResolving
});
}
}, randomNumberOfSeconds * 1000);
});
});
var testProimse = promiseTRRARNOSG();
testProimse.then(function(value) {
console.log("Value when promise is resolved : ", value);
});
testProimse.catch(function(reason) {
console.log("Reason when promise is rejected : ", reason);
});
// Let us loop through and create ten different promises using the function to see some variation. Some will be resolved and some will be rejected. 
for (i=1; i<=10; i++) {
let promise = promiseTRRARNOSG();
promise.then(function(value) {
console.log("Value when promise is resolved : ", value);
});
promise.catch(function(reason) {
console.log("Reason when promise is rejected : ", reason);
});
}

刷新浏览器页面并在控制台中运行代码以查看用于resolve方案和reject方案的不同输出。 展望未来,我们将看到我们如何创建多个promise并检查其输出,而无需执行此操作。

静态方法

Promise对象中有四种静态方法。

前两个是助手方法或快捷方式。 它们可帮助您轻松创建已解决或已拒绝的承诺。

Promise.reject(reason)

帮助您创建被拒绝的承诺。

var promise3 = Promise.reject("Not interested");
promise3.then(function(value){
console.log("This will not run as it is a resolved promise. The resolved value is ", value);
});
promise3.catch(function(reason){
console.log("This run as it is a rejected promise. The reason is ", reason);
});

Promise.resolve(value)

帮助您创建已解决的承诺。

var promise4 = Promise.resolve(1);
promise4.then(function(value){
console.log("This will run as it is a resovled promise. The resolved value is ", value);
});
promise4.catch(function(reason){
console.log("This will not run as it is a resolved promise", reason);
});

在旁注中,promise可以具有多个处理程序。 因此,您可以将上面的代码更新为

var promise4 = Promise.resolve(1);
promise4.then(function(value){
console.log("This will run as it is a resovled promise. The resolved value is ", value);
});
promise4.then(function(value){
console.log("This will also run as multiple handlers can be added. Printing twice the resolved value which is ", value * 2);
});
promise4.catch(function(reason){
console.log("This will not run as it is a resolved promise", reason);
});

和输出将看起来像。

了解JavaScript中的承诺

接下来的两种方法可帮助您处理一组承诺。 当您处理多个promise时,最好先创建一个promise数组,然后对promise集合进行必要的操作。 为了理解这些方法,我们将无法使用我们方便的promiseTRRARNOSG因为它太随机了。 最好有一些确定性的承诺,以便我们能够理解其行为。 让我们创建两个函数。 一种将在n秒后解决,另一种将在n秒后拒绝。

var promiseTRSANSG = (promiseThatResolvesAfterNSecondsGenerator = function(
n = 0
) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve({
resolvedAfterNSeconds: n
});
}, n * 1000);
});
});
var promiseTRJANSG = (promiseThatRejectsAfterNSecondsGenerator = function(
n = 0
) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject({
rejectedAfterNSeconds: n
});
}, n * 1000);
});
});

现在让我们使用这些帮助器功能来了解Promise.All

无极

根据MDN文档

Promise.all(iterable)方法返回一个Promise ,当iterable参数中的所有promise已解决或可迭代参数不包含promise时,该Promise进行解析。 它以第一个承诺被拒绝的理由拒绝。

情况1 :当所有的诺言都实现了。 这是最常用的方案。

console.time("Promise.All");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(1));
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(2));
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) {
console.timeEnd("Promise.All");
console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) {
console.log("One of the promises failed with the following reason", reason);
});
了解JavaScript中的承诺
所有的诺言都实现了。

我们通常需要从输出中得出两个重要的观察结果。

第一花费2秒的第三个诺言完成,而花费4秒的第二个诺言完成。 但是,正如您在输出中看到的那样,承诺的顺序将保留在值中。

第二我添加了一个控制台计时器,以找出Promise.All需要多长时间。 如果按顺序执行承诺,则总共应花费1 + 4 + 2 = 7秒。 但是从我们的计时器中我们看到只需要4秒。 这证明了所有诺言都是并行执行的。

情况2:没有承诺时。 我认为这是最不常用的。

console.time("Promise.All");
var promisesArray = [];
promisesArray.push(1);
promisesArray.push(4);
promisesArray.push(2);
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) {
console.timeEnd("Promise.All");
console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) {
console.log("One of the promises failed with the following reason", reason);
});
了解JavaScript中的承诺

由于数组中没有promise,因此将解决返回的promise。

情况3:以第一个承诺被拒绝的原因拒绝。

console.time("Promise.All");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(1));
promisesArray.push(promiseTRSANSG(5));
promisesArray.push(promiseTRSANSG(3));
promisesArray.push(promiseTRJANSG(2));
promisesArray.push(promiseTRSANSG(4));
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) {
console.timeEnd("Promise.All");
console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) {
console.timeEnd("Promise.All");
console.log("One of the promises failed with the following reason ", reason);
});
了解JavaScript中的承诺
第一次拒绝后,执行停止

无极种族

根据MDN文档

Promise.race(iterable)方法将返回一个可解决或拒绝的promise,该promise中的一个promise之一会解析或拒绝,并带有该promise的值或原因。

案例1:其中一个承诺首先解决。

console.time("Promise.race");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(3));
promisesArray.push(promiseTRSANSG(2));
promisesArray.push(promiseTRJANSG(3));
promisesArray.push(promiseTRSANSG(4));
var promisesRace = Promise.race(promisesArray);
promisesRace.then(function(values) {
console.timeEnd("Promise.race");
console.log("The fasted promise resolved", values);
});
promisesRace.catch(function(reason) {
console.timeEnd("Promise.race");
console.log("The fastest promise rejected with the following reason ", reason);
});
了解JavaScript中的承诺
最快的分辨率

所有的诺言都是并行进行的。 第三个承诺在2秒内解决。 一旦完成, Promise.race返回的Promise.race被解决。

情况2:其中一个承诺首先被拒绝。

console.time("Promise.race");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(6));
promisesArray.push(promiseTRSANSG(5));
promisesArray.push(promiseTRJANSG(3));
promisesArray.push(promiseTRSANSG(4));
var promisesRace = Promise.race(promisesArray);
promisesRace.then(function(values) {
console.timeEnd("Promise.race");
console.log("The fasted promise resolved", values);
});
promisesRace.catch(function(reason) {
console.timeEnd("Promise.race");
console.log("The fastest promise rejected with the following reason ", reason);
});
了解JavaScript中的承诺
最快的拒绝

所有的诺言都是并行进行的。 第四个承诺在3秒内被拒绝。 完成此操作后, Promise.race返回的承诺将被拒绝。

我已经编写了所有示例方法,以便可以测试各种场景,并且可以在浏览器本身中运行测试。 这就是在示例中看不到任何API调用,文件操作或数据库调用的原因。 尽管所有这些都是真实的示例,但是您需要付出更多的努力来设置它们并进行测试。 而使用延迟功能可为您提供类似的方案,而无需额外设置。 您可以轻松地使用这些值来查看和签出不同的方案。 您可以结合使用promiseTRJANSGpromiseTRSANSGpromiseTRRARNOSG方法来模拟足够的场景,以全面了解promiseTRRARNOSG 在相关块之前和之后也使用console.time方法将有助于我们轻松确定诺言是并行运行还是顺序运行。 让我知道您是否还有其他有趣的情况,或者我错过了什么。 如果您想将所有代码示例放在一个地方,请查看此要点。

了解JavaScript中的承诺

蓝鸟具有一些有趣的功能,例如

  1. Promise.prototype.timeout
  2. 承诺
  3. 承诺

我们将在另一篇文章中讨论这些内容。

我还将再写一篇关于我从异步和等待中学习的文章。

在结束之前,我想列出我遵循的所有拇指法则,以使我的头脑保持对诺言的理智。

使用诺言的拇指规则

  1. 每当您使用异步或阻塞代码时,请使用Promise。
  2. resolve映射到thenreject映射到catch所有的实际目的。
  3. 确保写入两根.catch.then对所有承诺的方法。
  4. 如果在两种情况下都需要执行某些操作,请使用.finally
  5. 改变每个诺言我们只有一枪。
  6. 我们可以将多个处理程序添加到一个Promise中。
  7. Promise对象中所有方法的返回类型(无论是静态方法还是原型方法)都是Promise
  8. Promise.all中,所有诺言的顺序都将保持在value变量中,而与首先解决哪个诺言Promise.all

请指出我是否在这里遗漏了一些东西,或者可以改善一些地方。 当我们在MERN堆栈上构建当前产品https://learningpaths.io/时,我开始花更多的时间在Javascript上。 我花了几天的时间在Google上搜索和阅读有关诺言的信息。 跟踪链接和注释真是一团糟。 因此,我们着手构建一个chrome扩展程序,该扩展程序将完全解决此问题并帮助您组织学习路径。 它仍处于Alpha阶段。 我们将与选定的100个人分享它,他们希望尝试一下并向我们提供反馈。 如果您有兴趣,请访问https://learningpaths.io/,并告诉我们您有兴趣获得早期访问权。 如果您认为您的朋友或同事有兴趣,请传播。

如果您喜欢这篇文章并想阅读类似的文章,请别忘了鼓掌。

了解JavaScript中的承诺
单击并拖动以拍手不止一次。 限制为50。

理解系列

本文是我的学习系列(称为“ 理解X”)的一部分 您可以在此链接中阅读更多内容

了解Javascript

了解Linux

了解React

了解学习

了解加密货币

From: https://hackernoon.com/understanding-promises-in-javascript-13d99df067c1