理解 Javascript 中的 Promise

阿里云最近在作活动,低至2折,有兴趣能够看看:
https://promotion.aliyun.com/...

为了保证的可读性,本文采用意译而非直译。javascript

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!html

图片描述

背景

当你第一次开始使用JavaScript时,它会有点使人沮丧。 你会听到有些人说JavaScript是同步编程语言,而其余人则认为它是异步的。 你会听到阻塞代码,非阻塞代码,事件驱动设计模式,事件生命周期,函数堆栈,事件队列,冒泡,polyfill,babel,angular,reactJS,vueJS 以及大量其余工具和库。 不要烦恼,你不是第一个。 这也有一个术语,它被称为 JavaScript 疲劳。前端

JavaScript疲劳: 当人们使用不须要的工具来解决他们没有的问题时,就会出现JavaScript疲劳

JavaScript是一种同步编程语言。可是因为回调函数,咱们可使它像异步编程语言同样工做。vue

初学者的 Promise

JavaScript中的 promise 与现实生活中的承诺很是类似。首先让咱们看看现实生活中的承诺。java

promise 词典中的定义:保证某人会作某事或某件事会发生。react

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

  1. 承诺给你一个将会有所做为的保证。他们(作出承诺的人)是本身作仍是别人作,这可有可无。他们给你一个保证,在此基础上你能够计划一些事情。
  2. 诺言能够遵照,也能够违背。
  3. 当一个承诺被遵照时,你指望从这个承诺中获得什么。你能够利用承诺的结果来作进一步的行动或计划。
  4. 当一个承诺被打破时,你会想知道为何作出承诺的人不能履行他的承诺。一旦你知道了缘由,并确认承诺已经被打破,你就能够计划下一步要作什么或如何处理它。
  5. 在做出承诺的时候,咱们所拥有的只是一种保证。咱们不能当即采起行动。咱们能够决定和制定当承诺被遵照(所以咱们有了预期的结果)或违背(咱们知道缘由,所以咱们能够计划一个意外事件)时须要作什么。
  6. 有可能你根本就没有收到承诺人的回复。在这种状况下,您宁愿保留一个时间阈值。若是承诺的人10天后不回来找我,我会认为他有一些问题,不会遵照他的承诺。因此即便那我的在15天后回到你身边,这对你来讲也再也不重要,由于你已经有了其余的计划。

JavaScript中的 Promise

根据经验,对于JavaScript,我老是阅读来自MDN Web文档的文档。在全部资源中,我认为它们提供了最简洁的细节。我阅读了来自 MDSN Web文档的promise 介绍,并尝试了一些代码来掌握它。github

理解承诺有两个部分。“建立 promises” 和 “处理 promises”。虽然咱们的大多数代码一般会迎合其余库建立的 promises 的处理,但彻底理解这些 promises 确定会有所帮助。一旦你过了初学阶段,理解“创造promises ”一样重要。chrome

Promise 建立

让咱们看一下建立新promis的语法编程

图片描述

构造函数接受一个名为executor 的函数。 此执行函数接受两个参数 resolve 和 reject,它们f都是函数。 Promise 一般用于更容易处理异步操做或阻塞代码,其示例包括文件操做,API调用,DB调用,IO调用等。这些异步操做的启动发生在执行函数中。若是异步操做成功,则经过 promise 的建立者调用resolve 函数返回预期结果,一样,若是出现意外错误,则经过调用 reject 函数传递错误具体信息。

图片描述

var keepsHisWord;
keepsHisWord = true;
promise1 = new Promise(function(resolve, reject) {
  if (keepsHisWord) {
    resolve("小智承诺坚持分享好的东西给你们");
  } else {
    reject("我没有作到!");
  }
});
console.log(promise1);

图片描述

因为该 promise 会当即执行,咱们将没法检查该 promise 的初始状态。所以,让咱们创造一个须要时间来执行的 promise,最简单的方法是使用 setTimeOut 函数。

图片描述

promise2 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve({
      message: "小智承诺坚持分享好的东西给你们",
      code: "200"
    });
  }, 10 * 1000);
});
console.log(promise2);

上面的代码只是建立了一个在10秒后无条件执行 promise。 所以,咱们能够检查promise 的状态,直到它被 resolve。

图片描述

一旦十秒钟过去,promise 就会执行 resolve。PromiseStatus 和 PromiseValue 都会相应地更新。如你所见,咱们更新了 resolve 函数,以便传递 JSON 对象,而不是简单的字符串。这只是为了说明咱们也能够在 resolve 函数中传递其余值。

图片描述

如今让咱们看看一个 promise reject的例子。咱们只要修改一下 keepsHisWord:

图片描述

图片描述

咱们能够看到 PromiseStatus 能够有三个不一样的值,pending(进行中)、 resolved(已成功) 或 rejected(失败)。建立 promise 时,PromiseStatus 将处于 pending 状态,而且 PromiseValue 为 undefined,直到 promise 被 resolved 或 rejected 为止。 当 promise 处于 resolved 或 rejected 的状态时,就称为 settled(已定型)。 因此 promise 一般从 pending 态转换到 settled 状态。

既然咱们知道 promise 是如何建立的,咱们就能够看看如何使用或处理 promise。这将与理解 Promise 对象密切相关。

理解 Promise 对象

根据 MDN 文档

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

Promise 对象具备静态方法和原型方法,Promise 对象中的静态方法能够独立应用,而原型方法须要应用于 Promise对象的实例。记住,普通方法和原型都返回一个 romise,这使得理解事物变得容易得多。

原型方法(Prototype Methods)

让咱们首先从原型方法开始,有三种方法。重申一下,记住全部这些方法均可以应用于 Promise 对象的一个实例,而且全部这些方法依次返回一个 Promise。如下全部方法都为 promise 的不一样状态转换分配处理程序。正如咱们前面看到的,当建立一个 Promise 时,它处于 pending 状态。Promise 根据是否 fulfilled(已成功)或rejected(已失败),将运行如下三种方法中的一种或多种。

Promise.prototype.catch(onRejected)

Promise.prototype.then(onFulfilled, onRejected)

Promise.prototype.finally(onFinally)

下图显示了 then 和 .catch 方法的流程。因为它们返回一个 Promise ,它们能够再次被链式调用。无论 promise 最后的状态,在执行完t hen 或 catch 指定的回调函数之后,都会执行finally方法指定的回调函数。

图片描述

这里有一个小故事。你是一个上学的孩子,你问你的妈妈要一个电话。她说:“这个月底我要买一部手机。”

让咱们看看,若是承诺在月底执行,JavaScript中会是什么样子。

图片描述

var momsPromise = new Promise(function(resolve, reject) {
  momsSavings = 20000;
  priceOfPhone = 60000;
  if (momsSavings > priceOfPhone) {
    resolve({
      brand: "iphone",
      model: "6s"
    });
  } else {
    reject("咱们没有足够的储蓄,让咱们多存点钱吧。");
  }
});
momsPromise.then(function(value) {
  console.log("哇,我获得这个电话做为礼物 ", JSON.stringify(value));
});
momsPromise.catch(function(reason) {
  console.log("妈妈不能给我买电话,由于 ", reason);
});
momsPromise.finally(function() {
  console.log(
    "无论妈妈能不能给我买个电话,我仍然爱她"
  );
});

输出:

图片描述

若是咱们把妈妈的礼物价值改成20万美圆,那么妈妈就能够给儿子买礼物了。在这种状况下,输出将是

图片描述

接着 then方法的第一个参数是 resolved 状态的回调函数,第二个参数(可选)是 rejected 状态的回调函数。因此咱们也能够这样写:

图片描述

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

为了确保咱们能够在浏览器中运行全部这些示例,或者在chrome中运行特定的示例,我要确保咱们的代码示例中没有外部依赖关系。

为了更好地理解进一步的主题,让咱们建立一个函数,该函数将返回一个 Promise,函数里随机执行 resolve 或者 rejected ,以便咱们能够测试各类场景。

因为咱们须要随机数,让咱们先建立一个随机函数,它将返回x和y之间的随机数。

图片描述

让咱们建立一个函数,它将为咱们返回 promise。让咱们调用 promiseTRRARNOSG 函数,它是promiseThatResolvesRandomlyAfterRandomNumnberOfSecondsGenerator 的别名。这个函数将建立一个 promise,该 promise 将在 2 到 10 秒之间的随机数秒后执行 resolve 或 reject。为了随机执行resolve 和 reject,咱们将建立一个介于 1 和 10 之间的随机数。若是生成的随机数大于 5,咱们将执行 resolve ,不然执行 reject。

图片描述

function getRandomNumber(start = 1, end = 10) {
  //works when both start and end are >=1
  return (parseInt(Math.random() * end) % (end - start + 1)) + start;
}
var promiseTRRARNOSG = (promiseThatResolvesRandomlyAfterRandomNumnberOfSecondsGenerator = function() {
  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);
});
// 建立10个不一样的promise
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对象中有四种静态方法。

前两个是帮助方法或快捷方式。 它们能够帮助您轻松建立 resolved 和 reject 方法。

Promise.reject(reason)

粟子:

图片描述

Promise.resolve(value)

粟子:

图片描述

在旁注上,promise 能够有多个处理程序。所以,你能够将上述代码更新为:

图片描述

输出:

图片描述

下面两个方法帮助你处理一组 promise 。当你处理多个promise 时,最好先建立一个promise 数组,而后对这些promise 集执行必要的操做。

为了理解这些方法,咱们不能使用上例中的 promiseTRRARNOSG,由于它太随机了,最好有一些肯定性的 promise ,这样咱们才能更好理解 promise 行为。

让咱们建立两个函数。一个会在n秒后执行resolve,另外一个会在n秒后执行 reject。

图片描述

如今让咱们使用这些帮助函数来理解 Promise.All

Promise.All

根据 MDN 文档:

Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内全部的 promise
都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);若是参数中 promise
有一个失败(rejected),此实例回调失败(reject),失败缘由的是第一个失败 promise 的结果。

例一:当全部的 promise 都执行完成了,这是最经常使用的场景。

图片描述

图片描述

咱们须要从输出中得出两个重要的结论:

  1. 第三个 promise 须要2秒完成,第二个 promise 须要4秒。可是正如你在输出中看到的,promise
    的顺序在值中保持不变。
  2. 我添加了一个计时器来肯定 promise 执行所须要时间。若是 promise 是按顺序执行的,那么总共须要 1+4+2=7 秒,可是从计时器中咱们看到它只须要4秒。这证实了全部的 promise 都是并行执行的。

例二:当数组不是 promise 的时候呢?(我认为这是最不经常使用的)

图片描述

输出:

图片描述

因为数组中没有 promise,所以将执行 promise 中的 resolve。

例一:其中一个 promise 状态最早为 resolve 状态

图片描述

图片描述

Promise.race

根据 MDN:

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
const p = Promise.race([p1, p2, p3]);

上面代码中,只要p一、p二、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。

图片描述

图片描述

全部的 promise 都是并行运行的。第三个 promise 在 2 秒内完成,因此是最早改变的就返回给Promise.race。

例二:其中一个 promise 状态最早为 reject 状态

图片描述

图片描述

全部的 promise 都是并行运行的。第四个 promise 在 3 秒内完成,因此是最早改变的就返回给Promise.race。

使用 promise 的经验法则

  1. 使用异步或阻塞代码时,请使用 promise。
  2. 为了代码的可读性,resolve 方法对待 then, reject 对应 catch 。
  3. 确保同时写入.catch 和 .then 方法来实现全部的 promise。
  4. 若是在这两种状况下都须要作一些事情,请使用 .finally。
  5. 咱们只有一次改变每一个promise (单一原则)。
  6. 咱们能够在一个promise 中添加多个处理程序。
  7. Promise对象中全部方法的返回类型,不管是静态方法仍是原型方法,都是Promise。
  8. 在Promise.all中,不管哪一个promise 首先未完成,promise 的顺序都保持在值变量中。

原文:https://hackernoon.com/unders...

你的点赞是我持续分享好东西的动力,欢迎点赞!

一个笨笨的码农,个人世界只能终身学习!

交流

干货系列文章汇总以下,以为不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」做者,对前端技术保持学习爱好者。我会常常分享本身所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,便可看到福利,你懂的。

clipboard.png

相关文章
相关标签/搜索