资讯专栏INFORMATION COLUMN

自己动手实现一个Promise

Yujiaao / 3343人阅读

摘要:意味着操作成功完成。状态的对象可能触发状态并传递一个值给相应的状态处理方法,也可能触发失败状态并传递失败信息。测试用例测试用例方法返回一个带有拒绝原因参数的对象。

Promise基本用法

Promise 对象是一个代理对象,被代理的值在Promise对象创建时可能是未知的。

它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象

一个 Promise有以下几种状态:

</>复制代码

  1. pending: 初始状态,既不是成功,也不是失败状态。
  2. fulfilled: 意味着操作成功完成。
  3. rejected: 意味着操作失败。

pending 状态的 Promise 对象可能触发fulfilled 状态并传递一个值给相应的状态处理方法,也可能触发失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。

因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回promise 对象, 所以它们可以被链式调用。

</>复制代码

  1. var promise1 = new Promise(function(resolve, reject) {
  2. setTimeout(function() {
  3. resolve("foo");
  4. }, 300);
  5. });
  6. promise1.then(function(value) {
  7. console.log(value);
  8. // expected output: "foo"
  9. });
  10. console.log(promise1);
  11. // expected output: [object Promise]
Promise/A+

然后我们来了解一下Promise规范
Promises/A+规范(英文版)
Promises/A+规范(中文版)

Promise的实现

Promise是通过new创建的,可以通过构造函数模式或者是ES6的class来实现,这儿选择构造函数的方式来实现Promise,首先先完成一个简易版本的Promise。

</>复制代码

  1. function Promise(exector) {
  2. var _this = this
  3. this.status = "pending"
  4. this.value = undefined
  5. try {
  6. exector(resolve, reject)
  7. }catch(e) {
  8. reject(e)
  9. }
  10. function resolve(value) {
  11. if(_this.status === "pending") {
  12. _this.status = "fulfilled"
  13. _this.value = value
  14. }
  15. }
  16. function reject(value) {
  17. if(_this.status === "pending") {
  18. _this.status = "rejected"
  19. _this.value = value
  20. }
  21. }
  22. }
  23. Promise.prototype.then = function(resolveCallback, rejectCallback) {
  24. if(this.status === "fulfilled") {
  25. resolve(this.value)
  26. }
  27. if(this.status === "rejected") {
  28. reject(this.value)
  29. }
  30. }
  31. new Promise((resolve, reject)=> {
  32. resolve("1")
  33. }).then((data)=> {
  34. console.log("resolve" + data)
  35. }, (data)=> {
  36. console.log("reject" + data)
  37. }) //resolve1
  38. new Promise((resolve, reject)=> {
  39. setTimeout(()=> {
  40. resolve("1")
  41. }, 1000)
  42. }).then((data)=> {
  43. console.log("resolve" + data)
  44. }, (data)=> {
  45. console.log("reject" + data)
  46. }) //无法正常输出

上面这个promise中resolve和reject在同步的情况下都能正常输出,但是现在却不支持异步,因为在异步的时候调用then的时候状态还是"pending",所以resolve/reject并不能如期执行。

这个时候就需要一个存放后续调用事件的数组,当异步函数执行完毕后再执行数组中的函数。

改进后异步方法可以正常运行:

</>复制代码

  1. function Promise(exector) {
  2. var _this = this
  3. this.status = "pending"
  4. this.value = undefined
  5. this.resolveList = []
  6. this.rejectList = []
  7. try {
  8. exector(resolve, reject)
  9. }catch(e) {
  10. reject(e)
  11. }
  12. function resolve(value) {
  13. if(_this.status === "pending") {
  14. _this.status = "fulfilled"
  15. _this.value = value
  16. _this.resolveList.forEach(item=> {
  17. item(_this.value)
  18. _this.resolveList.shift()
  19. })
  20. }
  21. }
  22. function reject(value) {
  23. if(_this.status === "pending") {
  24. _this.status = "rejected"
  25. _this.value = value
  26. _this.rejectList.forEach(item=> {
  27. item(_this.value)
  28. _this.rejectList.shift()
  29. })
  30. }
  31. }
  32. }
  33. Promise.prototype.then = function(resolveCallback, rejectCallback) {
  34. if(this.status === "fulfilled") {
  35. resolve(this.value)
  36. }
  37. if(this.status === "rejected") {
  38. reject(this.value)
  39. }
  40. if(this.status === "pending") {
  41. this.resolveList.push(resolveCallback)
  42. this.rejectList.push(rejectCallback)
  43. }
  44. }
  45. new Promise((resolve, reject)=> {
  46. setTimeout(()=> {
  47. resolve("1")
  48. }, 1000)
  49. }).then((data)=> {
  50. console.log("resolve" + data)
  51. }, (data)=> {
  52. console.log("reject" + data)
  53. })
链式调用

我们可以注意到Promise是可以链式调用的,这就需要then的方法返回一个Promise对象。

下面是一个链式调用的简单例子:

</>复制代码

  1. let promise = new Promise((resolve, reject)=> {
  2. resolve(666)
  3. })
  4. promise.then(data=> {
  5. console.log(data)
  6. return data + 1
  7. }).then(data=> {
  8. console.log(data)
  9. })
  10. //666
  11. //667

在Promise链中返回Promise:

</>复制代码

  1. let promise1 = new Promise((resolve, reject)=> {
  2. resolve(666)
  3. })
  4. let promise2 = new Promise((resolve, reject)=> {
  5. resolve(999)
  6. })
  7. promise1.then(data=> {
  8. console.log(data)
  9. return promise2
  10. }).then(data=> {
  11. console.log(data)
  12. })
  13. //666
  14. //999

关于这种写法需要注意的是,第二个完成处理程序被添加到第三个promise而不是return的promise2,上面的例子等价于:

</>复制代码

  1. let promise1 = new Promise((resolve, reject)=> {
  2. resolve(666)
  3. })
  4. let promise2 = new Promise((resolve, reject)=> {
  5. resolve(999)
  6. })
  7. let promise3 = promise1.then(data=> {
  8. console.log(data)
  9. return promise2
  10. })
  11. promise3.then(data=> {
  12. console.log(data)
  13. })
  14. //666
  15. //999

当异步的时候调用then函数的时候状态为pending,这个时候同样需要返回一个promise方便后续的链式调用。

所以修改为链式调用后的代码为:

</>复制代码

  1. function Promise(exector) {
  2. var _this = this
  3. this.status = "pending"
  4. this.value = undefined
  5. this.resolveList = []
  6. this.rejectList = []
  7. try {
  8. exector(resolve, reject)
  9. }catch(e) {
  10. reject(e)
  11. }
  12. function resolve(value) {
  13. if(_this.status === "pending") {
  14. _this.status = "fulfilled"
  15. _this.value = value
  16. _this.resolveList.forEach(item=> {
  17. item(_this.value)
  18. _this.resolveList.shift()
  19. })
  20. }
  21. }
  22. function reject(value) {
  23. if(_this.status === "pending") {
  24. _this.status = "rejected"
  25. _this.value = value
  26. _this.rejectList.forEach(item=> {
  27. item(_this.value)
  28. _this.rejectList.shift()
  29. })
  30. }
  31. }
  32. }
  33. Promise.prototype.then = function(resolveCallback, rejectCallback) {
  34. var _this = this
  35. if(this.status === "fulfilled") {
  36. return new Promise((resolve, reject)=> {
  37. var result = resolveCallback(_this.value)
  38. if(result instanceof Promise) {
  39. result.then(resolve, reject)
  40. }else {
  41. resolve(result)
  42. }
  43. })
  44. }
  45. if(this.status === "rejected") {
  46. return new Promise((resolve, reject)=> {
  47. var result = rejectCallback(_this.value)
  48. if(result instanceof Promise) {
  49. result.then(resolve, reject)
  50. }else {
  51. reject(result)
  52. }
  53. })
  54. }
  55. if(this.status === "pending") {
  56. return new Promise((resolve, reject)=> {
  57. _this.resolveList.push(function() {
  58. var result = resolveCallback(_this.value)
  59. if(result instanceof Promise) {
  60. result.then(resolve, reject)
  61. }else {
  62. resolve(result)
  63. }
  64. })
  65. _this.rejectList.push(function() {
  66. var result = rejectCallback(_this.value)
  67. if(result instanceof Promise) {
  68. result.then(resolve, reject)
  69. }else {
  70. reject(result)
  71. }
  72. })
  73. })
  74. }
  75. }
  76. new Promise((resolve, reject)=> {
  77. resolve(666)
  78. }).then((data)=> {
  79. console.log("resolve1:" + data)
  80. return 999
  81. }).then((data)=> {
  82. console.log("resolve2:" + data)
  83. })
  84. //resolve1: 666
  85. //resolve2: 999
  86. new Promise((resolve, reject)=> {
  87. resolve(666)
  88. }).then((data)=> {
  89. console.log("resolve1:" + data)
  90. return new Promise((resolve, reject)=> {
  91. resolve(999)
  92. })
  93. }).then((data)=> {
  94. console.log("resolve2:" + data)
  95. })
  96. //resolve1: 666
  97. //resolve2: 999

基本的功能已经实现,下面开始实现Promise的all,race,resolve,reject方法,链式调用部分思路借鉴Promise/A+规范的实现

Promise.all()

该方法只接受一个有多个受监听的Promise的可迭代对象(比如数组),只有当可迭代对中所有Promise都被解决后才会返回resolve,如果参数中 promise 有一个失败,此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

</>复制代码

  1. Promise.all = function(iterable) {
  2. return new Promise((resolve, reject) => {
  3. let result = []
  4. for(const item of iterable) {
  5. item.then(data => {
  6. result.push(data)
  7. }, reason=> {
  8. result = reason
  9. return
  10. })
  11. }
  12. resolve(result)
  13. })
  14. }
  15. //下面是测试用例
  16. let p1 = new Promise((resolve, reject) => {
  17. resolve(666)
  18. })
  19. let p2 = new Promise((resolve, reject) => {
  20. resolve(888)
  21. })
  22. let p3 = new Promise((resolve, reject) => {
  23. resolve(999)
  24. })
  25. let p6 = new Promise((resolve, reject) => {
  26. reject(222)
  27. })
  28. let p4 = Promise.all([p1, p2, p3])
  29. p4.then(data => {
  30. console.log(data)
  31. })
  32. //[666, 888, 999]
  33. let p7 = Promise.all([p1, p3, p6])
  34. p7.then(data => {
  35. console.log(data)
  36. })
  37. //222
Promise.race()

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

</>复制代码

  1. Promise.race = function(iterable) {
  2. return new Promise((resolve, reject) => {
  3. for(const item of iterable) {
  4. item.then(data => {
  5. resolve(data)
  6. }, reason=> {
  7. reject(reson)
  8. })
  9. }
  10. })
  11. }
  12. //测试用例
  13. var p1 = new Promise(function(resolve, reject) {
  14. setTimeout(resolve, 500, "one");
  15. });
  16. var p2 = new Promise(function(resolve, reject) {
  17. setTimeout(resolve, 100, "two");
  18. });
  19. Promise.race([p1, p2]).then(function(value) {
  20. console.log(value);
  21. // Both resolve, but promise2 is faster
  22. });
  23. //two
Promise.resolve()

</>复制代码

  1. Promise.resolve = function(data) {
  2. return new Promise((resolve, reject) => {
  3. resolve(data)
  4. })
  5. }
  6. //测试用例
  7. var p1 = Promise.resolve(123);
  8. p1.then(function(value) {
  9. console.log(value);
  10. });
  11. //123
Promise.reject()

Promise.reject(reason)方法返回一个带有拒绝原因reason参数的Promise对象。

</>复制代码

  1. Promise.resolve = function(data) {
  2. return new Promise((resolve, reject) => {
  3. reject(data)
  4. })
  5. }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108992.html

相关文章

  • 自己动手,为vue写一个jsonp的方法

    摘要:它之后能够被使用到很多场景中其他处理请求和响应的方式,甚至任何需要生成自己的响应的方式。总结到这里都讲完了,其实没什么难度,主要是自己项目中遇到了,但是中没有这个方法啊。所以就想着实现了一个,因为其他的方法也都封装,不差这一个了。 Fetch 提供了对 Request 和 Response (以及其他与网络请求有关的)对象通用的定义。它之后能够被使用到很多场景中:service wor...

    dongxiawu 评论0 收藏0
  • 动手一个Promise

    摘要:一个后可以通过方法,指定和时的回调函数。构造函数内部要有一个值,用来保存上次执行的结果值,如果报错,则保存的是异常信息。因为是一个构造函数,使用的写法,首先想到的就是有显式声明的。 showImg(https://segmentfault.com/img/bVbffEu?w=530&h=253); Javascript语言的执行环境是单线程(single thread)。所谓单线程,就...

    shadajin 评论0 收藏0
  • 动手一个Promise

    摘要:一个后可以通过方法,指定和时的回调函数。构造函数内部要有一个值,用来保存上次执行的结果值,如果报错,则保存的是异常信息。因为是一个构造函数,使用的写法,首先想到的就是有显式声明的。 showImg(https://segmentfault.com/img/bVbffEu?w=530&h=253); Javascript语言的执行环境是单线程(single thread)。所谓单线程,就...

    NervosNetwork 评论0 收藏0
  • 解析 Promise 原理,实现一个Promise

    摘要:解析原理,实现一个概述这篇文章旨在解析的异步实现原理,并且以中的为蓝本实现一个简单的。具体的规范可以参见细节构造器中必须传入函数,否则会抛出错误。中的回调返回值会影响返回的对象。执行器传入构造器的为函数,并且在构造时就会执行。 解析 Promise 原理,实现一个Promise 概述 这篇文章旨在解析 Promise的异步实现原理,并且以 ES6中的 Promise 为蓝本实现一个简单...

    silenceboy 评论0 收藏0

发表评论

0条评论

Yujiaao

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<