1.什么是Promise?

Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一

2.对于几种常见异步编程方案

  • 回调函数
  • 事件监听
  • 发布/订阅
  • Promise对象

这里就拿回调函数说说

(1) 对于回调函数 我们用Jquery的ajax获取数据时 都是以回调函数方式获取的数据

  1. $.get(url, (data) => {
  2. console.log(data)
  3. )

(2) 如果说 当我们需要发送多个异步请求 并且每个请求之间需要相互依赖 那这时 我们只能 以嵌套方式来解决 形成 "回调地狱"

  1. $.get(url, data1 => {
  2. console.log(data1)
  3. $.get(data1.url, data2 => {
  4. console.log(data1)
  5. })
  6. })

这样一来,在处理越多的异步逻辑时,就需要越深的回调嵌套,这种编码模式的问题主要有以下几个:

  • 代码逻辑书写顺序与执行顺序不一致,不利于阅读与维护。
  • 异步操作的顺序变更时,需要大规模的代码重构。
  • 回调函数基本都是匿名函数,bug 追踪困难。
  • 回调函数是被第三方库代码(如上例中的 ajax )而非自己的业务代码所调用的,造成了 IoC 控制反转。

Promise 处理多个相互关联的异步请求

(1) 而我们Promise 可以更直观的方式 来解决 "回调地狱"

  1. const request = url => {
  2. return new Promise((resolve, reject) => {
  3. $.get(url, data => {
  4. resolve(data)
  5. });
  6. })
  7. };
  8.  
  9. // 请求data1
  10. request(url).then(data1 => {
  11. return request(data1.url);
  12. }).then(data2 => {
  13. return request(data2.url);
  14. }).then(data3 => {
  15. console.log(data3);
  16. }).catch(err => throw new Error(err));

(2) 相信大家在 vue/react 都是用axios fetch 请求数据 也都支持 Promise API

  1. import axios from 'axios';
  2. axios.get(url).then(data => {
  3. console.log(data)
  4. })

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

3.Promise使用

Promise 是一个构造函数, new Promise 返回一个 promise对象 接收一个excutor执行函数作为参数, excutor有两个函数类型形参resolve reject

  1. const promise = new Promise((resolve, reject) => {
  2. // 异步处理
  3. // 处理结束后、调用resolve 或 reject
  4. });

promise相当于一个状态机

promise的三种状态

  • pending
  • fulfilled
  • rejected

(1) promise 对象初始化状态为 pending

(2) 当调用resolve(成功),会由pending => fulfilled

(3) 当调用reject(失败),会由pending => rejected

注意promsie状态 只能由 pending => fulfilled/rejected, 一旦修改就不能再变

promise对象方法

(1) then方法注册 当resolve(成功)/reject(失败)的回调函数

  1. // onFulfilled 是用来接收promise成功的值
  2. // onRejected 是用来接收promise失败的原因
  3. promise.then(onFulfilled, onRejected);

注意:then方法是异步执行的

(2) resolve(成功) onFulfilled会被调用

  1. const promise = new Promise((resolve, reject) => {
  2. resolve('fulfilled'); // 状态由 pending => fulfilled
  3. });
  4. promise.then(result => { // onFulfilled
  5. console.log(result); // 'fulfilled'
  6. }, reason => { // onRejected 不会被调用
  7.  
  8. })

(3) reject(失败) onRejected会被调用

  1. const promise = new Promise((resolve, reject) => {
  2. reject('rejected'); // 状态由 pending => rejected
  3. });
  4. promise.then(result => { // onFulfilled 不会被调用
  5.  
  6. }, reason => { // onRejected
  7. console.log(rejected); // 'rejected'
  8. })

(4) promise.catch

在链式写法中可以捕获前面then中发送的异常,

  1. romise.catch(onRejected)
  2. 相当于
  3. promise.then(null, onRrejected);
  4.  
  5. // 注意
  6. // onRejected 不能捕获当前onFulfilled中的异常
  7. promise.then(onFulfilled, onRrejected);
  8.  
  9. // 可以写成:
  10. promise.then(onFulfilled)
  11. .catch(onRrejected);

promise chain

promise.then方法每次调用 都返回一个新的promise对象 所以可以链式写法

  1. function taskA() {
  2. console.log("Task A");
  3. }
  4. function taskB() {
  5. console.log("Task B");
  6. }
  7. function onRejected(error) {
  8. console.log("Catch Error: A or B", error);
  9. }
  10.  
  11. var promise = Promise.resolve();
  12. promise
  13. .then(taskA)
  14. .then(taskB)
  15. .catch(onRejected) // 捕获前面then方法中的异常

Promise的静态方法

(1) Promise.resolve 返回一个fulfilled状态的promise对象

  1. Promise.resolve('hello').then(function(value){
  2. console.log(value);
  3. });
  4.  
  5. Promise.resolve('hello');
  6. // 相当于
  7. const promise = new Promise(resolve => {
  8. resolve('hello');
  9. });

(2) Promise.reject 返回一个rejected状态的promise对象

  1. Promise.reject(24);
  2. new Promise((resolve, reject) => {
  3. reject(24);
  4. });

(3) Promise.all 接收一个promise对象数组为参数

只有全部为resolve才会调用 通常会用来处理 多个并行异步操作

  1. const p1 = new Promise((resolve, reject) => {
  2. resolve(1);
  3. });
  4.  
  5. const p2 = new Promise((resolve, reject) => {
  6. resolve(2);
  7. });
  8.  
  9. const p3 = new Promise((resolve, reject) => {
  10. reject(3);
  11. });
  12.  
  13. Promise.all([p1, p2, p3]).then(data => {
  14. console.log(data); // [1, 2, 3] 结果顺序和promise实例数组顺序是一致的
  15. }, err => {
  16. console.log(err);
  17. });

(4) Promise.race 接收一个promise对象数组为参数

Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。

  1. function timerPromisefy(delay) {
  2. return new Promise(function (resolve, reject) {
  3. setTimeout(function () {
  4. resolve(delay);
  5. }, delay);
  6. });
  7. }
  8. var startDate = Date.now();
  9.  
  10. Promise.race([
  11. timerPromisefy(10),
  12. timerPromisefy(20),
  13. timerPromisefy(30)
  14. ]).then(function (values) {
  15. console.log(values); //
  16. });

4.Promise 代码实现

  1. /**
  2. * Promise 实现 遵循promise/A+规范
  3. * Promise/A+规范译文:
  4. * https://malcolmyu.github.io/2015/06/12/Promises-A-Plus/#note-4
  5. */
  6.  
  7. // promise 三个状态
  8. const PENDING = "pending";
  9. const FULFILLED = "fulfilled";
  10. const REJECTED = "rejected";
  11.  
  12. function Promise(excutor) {
  13. let that = this; // 缓存当前promise实例对象
  14. that.status = PENDING; // 初始状态
  15. that.value = undefined; // fulfilled状态时 返回的信息
  16. that.reason = undefined; // rejected状态时 拒绝的原因
  17. that.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
  18. that.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
  19.  
  20. function resolve(value) { // value成功态时接收的终值
  21. if(value instanceof Promise) {
  22. return value.then(resolve, reject);
  23. }
  24.  
  25. // 为什么resolve 加setTimeout?
  26. // 2.2.4规范 onFulfilled 和 onRejected 只允许在 execution context 栈仅包含平台代码时运行.
  27. // 注1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
  28.  
  29. setTimeout(() => {
  30. // 调用resolve 回调对应onFulfilled函数
  31. if (that.status === PENDING) {
  32. // 只能由pedning状态 => fulfilled状态 (避免调用多次resolve reject)
  33. that.status = FULFILLED;
  34. that.value = value;
  35. that.onFulfilledCallbacks.forEach(cb => cb(that.value));
  36. }
  37. });
  38. }
  39.  
  40. function reject(reason) { // reason失败态时接收的拒因
  41. setTimeout(() => {
  42. // 调用reject 回调对应onRejected函数
  43. if (that.status === PENDING) {
  44. // 只能由pedning状态 => rejected状态 (避免调用多次resolve reject)
  45. that.status = REJECTED;
  46. that.reason = reason;
  47. that.onRejectedCallbacks.forEach(cb => cb(that.reason));
  48. }
  49. });
  50. }
  51.  
  52. // 捕获在excutor执行器中抛出的异常
  53. // new Promise((resolve, reject) => {
  54. // throw new Error('error in excutor')
  55. // })
  56. try {
  57. excutor(resolve, reject);
  58. } catch (e) {
  59. reject(e);
  60. }
  61. }
  62.  
  63. /**
  64. * resolve中的值几种情况:
  65. * 1.普通值
  66. * 2.promise对象
  67. * 3.thenable对象/函数
  68. */
  69.  
  70. /**
  71. * 对resolve 进行改造增强 针对resolve中不同值情况 进行处理
  72. * @param {promise} promise2 promise1.then方法返回的新的promise对象
  73. * @param {[type]} x promise1中onFulfilled的返回值
  74. * @param {[type]} resolve promise2的resolve方法
  75. * @param {[type]} reject promise2的reject方法
  76. */
  77. function resolvePromise(promise2, x, resolve, reject) {
  78. if (promise2 === x) { // 如果从onFulfilled中返回的x 就是promise2 就会导致循环引用报错
  79. return reject(new TypeError('循环引用'));
  80. }
  81.  
  82. let called = false; // 避免多次调用
  83. // 如果x是一个promise对象 (该判断和下面 判断是不是thenable对象重复 所以可有可无)
  84. if (x instanceof Promise) { // 获得它的终值 继续resolve
  85. if (x.status === PENDING) { // 如果为等待态需等待直至 x 被执行或拒绝 并解析y值
  86. x.then(y => {
  87. resolvePromise(promise2, y, resolve, reject);
  88. }, reason => {
  89. reject(reason);
  90. });
  91. } else { // 如果 x 已经处于执行态/拒绝态(值已经被解析为普通值),用相同的值执行传递下去 promise
  92. x.then(resolve, reject);
  93. }
  94. // 如果 x 为对象或者函数
  95. } else if (x != null && ((typeof x === 'object') || (typeof x === 'function'))) {
  96. try { // 是否是thenable对象(具有then方法的对象/函数)
  97. let then = x.then;
  98. if (typeof then === 'function') {
  99. then.call(x, y => {
  100. if(called) return;
  101. called = true;
  102. resolvePromise(promise2, y, resolve, reject);
  103. }, reason => {
  104. if(called) return;
  105. called = true;
  106. reject(reason);
  107. })
  108. } else { // 说明是一个普通对象/函数
  109. resolve(x);
  110. }
  111. } catch(e) {
  112. if(called) return;
  113. called = true;
  114. reject(e);
  115. }
  116. } else {
  117. resolve(x);
  118. }
  119. }
  120.  
  121. /**
  122. * [注册fulfilled状态/rejected状态对应的回调函数]
  123. * @param {function} onFulfilled fulfilled状态时 执行的函数
  124. * @param {function} onRejected rejected状态时 执行的函数
  125. * @return {function} newPromsie 返回一个新的promise对象
  126. */
  127. Promise.prototype.then = function(onFulfilled, onRejected) {
  128. const that = this;
  129. let newPromise;
  130. // 处理参数默认值 保证参数后续能够继续执行
  131. onFulfilled =
  132. typeof onFulfilled === "function" ? onFulfilled : value => value;
  133. onRejected =
  134. typeof onRejected === "function" ? onRejected : reason => {
  135. throw reason;
  136. };
  137.  
  138. // then里面的FULFILLED/REJECTED状态时 为什么要加setTimeout ?
  139. // 原因:
  140. // 其一 2.2.4规范 要确保 onFulfilled 和 onRejected 方法异步执行(且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行) 所以要在resolve里加上setTimeout
  141. // 其二 2.2.6规范 对于一个promise,它的then方法可以调用多次.(当在其他程序中多次调用同一个promise的then时 由于之前状态已经为FULFILLED/REJECTED状态,则会走的下面逻辑),所以要确保为FULFILLED/REJECTED状态后 也要异步执行onFulfilled/onRejected
  142.  
  143. // 其二 2.2.6规范 也是resolve函数里加setTimeout的原因
  144. // 总之都是 让then方法异步执行 也就是确保onFulfilled/onRejected异步执行
  145.  
  146. // 如下面这种情景 多次调用p1.then
  147. // p1.then((value) => { // 此时p1.status 由pedding状态 => fulfilled状态
  148. // console.log(value); // resolve
  149. // // console.log(p1.status); // fulfilled
  150. // p1.then(value => { // 再次p1.then 这时已经为fulfilled状态 走的是fulfilled状态判断里的逻辑 所以我们也要确保判断里面onFuilled异步执行
  151. // console.log(value); // 'resolve'
  152. // });
  153. // console.log('当前执行栈中同步代码');
  154. // })
  155. // console.log('全局执行栈中同步代码');
  156. //
  157.  
  158. if (that.status === FULFILLED) { // 成功态
  159. return newPromise = new Promise((resolve, reject) => {
  160. setTimeout(() => {
  161. try{
  162. let x = onFulfilled(that.value);
  163. resolvePromise(newPromise, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值
  164. } catch(e) {
  165. reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
  166. }
  167. });
  168. })
  169. }
  170.  
  171. if (that.status === REJECTED) { // 失败态
  172. return newPromise = new Promise((resolve, reject) => {
  173. setTimeout(() => {
  174. try {
  175. let x = onRejected(that.reason);
  176. resolvePromise(newPromise, x, resolve, reject);
  177. } catch(e) {
  178. reject(e);
  179. }
  180. });
  181. });
  182. }
  183.  
  184. if (that.status === PENDING) { // 等待态
  185. // 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
  186. return newPromise = new Promise((resolve, reject) => {
  187. that.onFulfilledCallbacks.push((value) => {
  188. try {
  189. let x = onFulfilled(value);
  190. resolvePromise(newPromise, x, resolve, reject);
  191. } catch(e) {
  192. reject(e);
  193. }
  194. });
  195. that.onRejectedCallbacks.push((reason) => {
  196. try {
  197. let x = onRejected(reason);
  198. resolvePromise(newPromise, x, resolve, reject);
  199. } catch(e) {
  200. reject(e);
  201. }
  202. });
  203. });
  204. }
  205. };
  206.  
  207. /**
  208. * Promise.all Promise进行并行处理
  209. * 参数: promise对象组成的数组作为参数
  210. * 返回值: 返回一个Promise实例
  211. * 当这个数组里的所有promise对象全部变为resolve状态的时候,才会resolve。
  212. */
  213. Promise.all = function(promises) {
  214. return new Promise((resolve, reject) => {
  215. let done = gen(promises.length, resolve);
  216. promises.forEach((promise, index) => {
  217. promise.then((value) => {
  218. done(index, value)
  219. }, reject)
  220. })
  221. })
  222. }
  223.  
  224. function gen(length, resolve) {
  225. let count = 0;
  226. let values = [];
  227. return function(i, value) {
  228. values[i] = value;
  229. if (++count === length) {
  230. console.log(values);
  231. resolve(values);
  232. }
  233. }
  234. }
  235.  
  236. /**
  237. * Promise.race
  238. * 参数: 接收 promise对象组成的数组作为参数
  239. * 返回值: 返回一个Promise实例
  240. * 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理(取决于哪一个更快)
  241. */
  242. Promise.race = function(promises) {
  243. return new Promise((resolve, reject) => {
  244. promises.forEach((promise, index) => {
  245. promise.then(resolve, reject);
  246. });
  247. });
  248. }
  249.  
  250. // 用于promise方法链时 捕获前面onFulfilled/onRejected抛出的异常
  251. Promise.prototype.catch = function(onRejected) {
  252. return this.then(null, onRejected);
  253. }
  254.  
  255. Promise.resolve = function (value) {
  256. return new Promise(resolve => {
  257. resolve(value);
  258. });
  259. }
  260.  
  261. Promise.reject = function (reason) {
  262. return new Promise((resolve, reject) => {
  263. reject(reason);
  264. });
  265. }
  266.  
  267. /**
  268. * 基于Promise实现Deferred的
  269. * Deferred和Promise的关系
  270. * - Deferred 拥有 Promise
  271. * - Deferred 具备对 Promise的状态进行操作的特权方法(resolve reject)
  272. *
  273. *参考jQuery.Deferred
  274. *url: http://api.jquery.com/category/deferred-object/
  275. */
  276. Promise.deferred = function() { // 延迟对象
  277. let defer = {};
  278. defer.promise = new Promise((resolve, reject) => {
  279. defer.resolve = resolve;
  280. defer.reject = reject;
  281. });
  282. return defer;
  283. }
  284.  
  285. /**
  286. * Promise/A+规范测试
  287. * npm i -g promises-aplus-tests
  288. * promises-aplus-tests Promise.js
  289. */
  290.  
  291. try {
  292. module.exports = Promise
  293. } catch (e) {
  294. }

5.Promise测试

  1. npm i -g promises-aplus-tests
  2. promises-aplus-tests Promise.js

6.相关知识参考资料

转载地址:https://juejin.im/post/5aa7868b6fb9a028dd4de672

Promise原理讲解 && 实现一个Promise对象 (遵循Promise/A+规范)的更多相关文章

  1. 浅谈Promise原理与应用

    在JavaScript中,所有代码都是单线程.由于该“缺陷”,JavaScript在处理网络操作.事件操作时都是需要进行异步执行的.AJAX就是一个典型的异步操作 对于异步操作,有传统的利用回调函数和 ...

  2. Promise原理—一步一步实现一个Promise

    promise特点 一个promise的当前状态只能是pending.fulfilled和rejected三种之一.状态改变只能是pending到fulfilled或者pending到rejected ...

  3. js 深入原理讲解系列-Promise

    js 深入原理讲解系列-Promise 能看懂这一题你就掌握了 js Promise 的核心原理 不要专业的术语,说人话,讲明白! Q: 输出下面 console.log 的正确的顺序? const ...

  4. 30分钟,让你彻底明白Promise原理

    前言 前一阵子记录了promise的一些常规用法,这篇文章再深入一个层次,来分析分析promise的这种规则机制是如何实现的.ps:本文适合已经对promise的用法有所了解的人阅读,如果对其用法还不 ...

  5. 10分钟,让你彻底明白Promise原理

    什么是Promise?本代码用定外卖来举例子,让你明白. // 定外卖就是一个Promise,Promist的意思就是承诺// 我们定完外卖,饭不会立即到我们手中// 这时候我们和商家就要达成一个承诺 ...

  6. Promise原理 && 简单实现

    Promise原理 参考https://github.com/chunpu/promise/blob/master/promise.js 个人认为原博的实现有点问题 在next函数的实现上, 会导致无 ...

  7. promise原理

      简介 Promise 对象用于延迟(deferred) 计算和异步(asynchronous )计算.一个Promise对象代表着一个还未完成,但预期将来会完成的操作.Promise 对象是一个返 ...

  8. Promise原理剖析

    传统的异步回调编程最大的缺陷是:回调地狱,由于业务逻辑非常复杂,代码串行请求好几层:并行请求以前也要通过引用step.async库实现.现在ES6推出了Promise,通过Promise的链式调用可以 ...

  9. Promise原理探究及实现

    前言 作为ES6处理异步操作的新规范,Promise一经出现就广受欢迎.面试中也是如此,当然此时对前端的要求就不仅仅局限会用这个阶段了.下面就一起看下Promise相关的内容. Promise用法及实 ...

随机推荐

  1. gc笔记2

    空间分配担保:在发生MinorGC之前,虚拟机会检查老年代最大连续可用是否大于新生代所有对象的空间,如果这个条件成立,则minorgc时安全的

  2. Java作业十二(2017-11-13)

    /*继承与抽象类*/ package com.baidu.www; abstract class Person { private String name; private int age; publ ...

  3. Batch入门教程丨第一章:部署与Hello World!(下)

    在上期分享的内容中,我们已经掌握了基础理论知识,今天我们将继续了解和学习与Windows Batch有关的知识和编程方法,如何编写和运行Windows Batch程序,脚本语言的入门方式等,从而能够更 ...

  4. [Swift]LeetCode673. 最长递增子序列的个数 | Number of Longest Increasing Subsequence

    Given an unsorted array of integers, find the number of longest increasing subsequence. Example 1: I ...

  5. Redis 设计与实现 (七)--事务

    事务 *ACID,指数据库事务正确执行的四个基本要素的缩写.包含:原子性(Atomicity).一致性(Consistency).隔离性(Isolation).持久性(Durability) redi ...

  6. SecureCRT8.1发送命令到多个或所有linux终端

  7. mysql之delete语法

    一:DELETE语法 以往用delect删除表数据是都是单表(一个表)删除.对于关联表,往往都是先删除第一个表的数据,然后再写另一个delect语句删除另一个表的数据(浪费时间,又影响性能,与数据库交 ...

  8. 使用 C# 代码实现拓扑排序

    0.参考资料 尊重他人的劳动成果,贴上参考的资料地址,本文仅作学习记录之用. https://www.codeproject.com/Articles/869059/Topological-sorti ...

  9. 【Redis篇】Redis持久化方式AOF和RDB

    一.前述 持久化概念:将数据从掉电易失的内存存放到能够永久存储的设备上. Redis持久化方式RDB(Redis DB)   hdfs:    fsimageAOF(AppendOnlyFile)   ...

  10. PyCharm证书过期:Your license has expired

    报错“your evaluation license has expired, pycharm will now exit”1.解决步骤,点击‘Activation code’,授权激活pycharm ...