什么是promise

Promise是异步编程的一种解决方案,promise就是一个对象,它可以获取异步操作的消息。从本意上讲,它是承诺,承诺过一段时间给你一个结果。 promise有三种状态:pedding(等待),fulfiled(成功),rejected(失败)。状态一旦改变,就不会在变。创造promise实例后,它会立即执行。

之前刚学习node的时候,会经常见到一个单词,callback,每一个异步里面都嵌套一个回调,当时有一些情况下其实就遇到了回调地狱的问题,也感觉到这样是有问题的,但是并没有去想怎么去解决。
promise就是用来解决这样的问题的:

  • 回调地狱,代码难以维护,常常第一个函数的输出是第二个函数的输入这种现象
  • promise可以支持多个并发的请求,获取并发请求中的数据
  • 这个promise可以解决异步的问题,本身不能说promise是异步的

怎么用promise

首先,我们来先看最基本的用法,new一个peomise对象

  1. let p = new Promise((resolve, reject) => {
  2. //异步操作
  3. setTimeout(() => {
  4. console.log('成功')
  5. resolve('成功')
  6. }, 1000)
  7. })

promise构造函数接受一个参数,函数,这个函数要传入两个参数

  • resolve:异步操作执行成功后的回调函数
  • reject:异步操作执行失败后的回调函数

链式操作

promise的精髓是状态,用维护状态,传递状态的放式来使得回调函数能够被即使调用,比传递callback函数要简单灵活。而promise真正的用法是在then的链式调用上面,这才是解决回调地狱的关键所在

  1. p.then(data => {
  2. console.log(data)
  3. }).then(data => {
  4. console.log(data)
  5. }).then(data => {
  6. console.log(data)
  7. })

reject

上面我们只是看了promise成功的状态,而失败的回调呢?

  1. let p = new Promise((resolve, reject) => {
  2. //做一些异步操作
  3. setTimeout(function(){
  4. var num = Math.ceil(Math.random()*10); //生成1-10的随机数
  5. if(num<=5){
  6. resolve(num);
  7. }
  8. else{
  9. reject('数字太大了');
  10. }
  11. }, 2000);
  12. });
  13. p.then((data) => {
  14. console.log('resolved',data);
  15. },(err) => {
  16. console.log('rejected',err);
  17. }
  18. );

我们知道promise构造函数中的函数接收两个参数,resolve是成功的回调,reject就是失败的回调。在then中,一样接受两个参数,一个是resolve成功的回调,一个是reject失败的回调。

catch

promise对象除了then方法之外,还有catch方法。catch我们是了解的,在promise中的用法也是一样的,运行中的错误就会被捕获到catch中

  1. p.then((data) => {
  2. console.log('resolved',data);
  3. console.log(somedata); //此处的somedata未定义
  4. })
  5. .catch((err) => {
  6. console.log('rejected',err);
  7. });

本来我们去访问一个不存在的变量,会直接报错,程序停止。但是在promise中错误被抛出,然后再catch中,捕获错误。和try catch的功能相同

all

当我们有多个异步方法,他们的执行顺序并没有强制的要求,可以同时的去调用。这时候可以用promise的all方法。all方法接受一个数组,数组包含每一个异步操作的promise对象。

  1. let Promise1 = new Promise(function(resolve, reject){})
  2. let Promise2 = new Promise(function(resolve, reject){})
  3. let Promise3 = new Promise(function(resolve, reject){})
  4. let p = Promise.all([Promise1, Promise2, Promise3])
  5. p.then(funciton(){
  6. // 三个都成功则成功
  7. }, function(){
  8. // 只要有失败,则失败
  9. })

all方法需要数组中的promise全部执行成功才会进入到成功状态,否则就是失败。

race

race方法和all一样,都是去并发请求多个异步操作,不同的是,all是全成功则成功,而race事看第一个返回的异步的状态,第一个成功则成功,第一个失败则失败

  1. //请求某个图片资源
  2. function requestImg(){
  3. var p = new Promise((resolve, reject) => {
  4. var img = new Image();
  5. img.onload = function(){
  6. resolve(img);
  7. }
  8. img.src = '图片的路径';
  9. });
  10. return p;
  11. }
  12. //延时函数,用于给请求计时
  13. function timeout(){
  14. var p = new Promise((resolve, reject) => {
  15. setTimeout(() => {
  16. reject('图片请求超时');
  17. }, 5000);
  18. });
  19. return p;
  20. }
  21. Promise.race([requestImg(), timeout()]).then((data) =>{
  22. console.log(data);
  23. }).catch((err) => {
  24. console.log(err);
  25. });

实现自己的promise

成功失败的回调方法

  1. class Promise {
  2. constructor (executor){
  3. //默认状态是等待状态
  4. this.status = 'pending';
  5. this.value = undefined;
  6. this.reason = undefined;
  7. //存放成功的回调
  8. this.onResolvedCallbacks = [];
  9. //存放失败的回调
  10. this.onRejectedCallbacks = [];
  11. let resolve = (data) => {//this指的是实例
  12. if(this.status === 'pending'){
  13. this.value = data;
  14. this.status = "resolved";
  15. this.onResolvedCallbacks.forEach(fn => fn());
  16. }
  17. }
  18. let reject = (reason) => {
  19. if(this.status === 'pending'){
  20. this.reason = reason;
  21. this.status = 'rejected';
  22. this.onRejectedCallbacks.forEach(fn => fn());
  23. }
  24. }
  25. try{//执行时可能会发生异常
  26. executor(resolve,reject);
  27. }catch (e){
  28. reject(e);//promise失败了
  29. }
  30. }

then方法

  1. then(onFulFilled, onRejected) {
  2. if (this.status === 'resolved') {
  3. onFulFilled(this.value);
  4. }
  5. if (this.status === 'rejected') {
  6. onRejected(this.reason);
  7. }
  8. // 当前既没有完成 也没有失败
  9. if (this.status === 'pending') {
  10. // 存放成功的回调
  11. this.onResolvedCallbacks.push(() => {
  12. onFulFilled(this.value);
  13. });
  14. // 存放失败的回调
  15. this.onRejectedCallbacks.push(() => {
  16. onRejected(this.reason);
  17. });
  18. }
  19. }

链式调用

  1. function resolvePromise(promise2,x,resolve,reject){
  2. //判断x是不是promise
  3. //规范中规定:我们允许别人乱写,这个代码可以实现我们的promise和别人的promise 进行交互
  4. if(promise2 === x){//不能自己等待自己完成
  5. return reject(new TypeError('循环引用'));
  6. };
  7. // x是除了null以外的对象或者函数
  8. if(x !=null && (typeof x === 'object' || typeof x === 'function')){
  9. let called;//防止成功后调用失败
  10. try{//防止取then是出现异常 object.defineProperty
  11. let then = x.then;//取x的then方法 {then:{}}
  12. if(typeof then === 'function'){//如果then是函数就认为他是promise
  13. //call第一个参数是this,后面的是成功的回调和失败的回调
  14. then.call(x,y => {//如果Y是promise就继续递归promise
  15. if(called) return;
  16. called = true;
  17. resolvePromise(promise2,y,resolve,reject)
  18. },r => { //只要失败了就失败了
  19. if(called) return;
  20. called = true;
  21. reject(r);
  22. });
  23. }else{//then是一个普通对象,就直接成功即可
  24. resolve(x);
  25. }
  26. }catch (e){
  27. if(called) return;
  28. called = true;
  29. reject(e)
  30. }
  31. }else{//x = 123 x就是一个普通值 作为下个then成功的参数
  32. resolve(x)
  33. }
  34. }
  35. then(onFulFilled, onRejected) {
  36. onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : y => y
  37. onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
  38. let promise2
  39. if(this.status === FULFILLED) {
  40. promise2 = new Promise((resolve, reject) => {
  41. setTimeout(() => {
  42. try {
  43. let x = onFulFilled(this.value)
  44. resolvePromise(promise2, x, resolve, reject)
  45. }catch(e) {
  46. reject(e)
  47. }
  48. }, 0)
  49. })
  50. // console.log('resolve then...', this.value)
  51. // onFulFilled(this.value)
  52. }
  53. if(this.status === REJECTED) {
  54. promise2 = new Promise((resolve, reject) => {
  55. setTimeout(() => {
  56. try {
  57. let x = onRejected(this.value)
  58. resolvePromise(promise2, x, resolve, reject)
  59. }catch(e) {
  60. reject(e)
  61. }
  62. }, 0)
  63. })
  64. // console.log('reject then...', this.reason)
  65. // onRejected(this.reason)
  66. }
  67. if(this.status === PENDING) {
  68. promise2 = new Promise((resolve, reject) => {
  69. this.onResolvedCallbacks.push(() => {
  70. setTimeout(() => {
  71. try{
  72. let x = onFuiFilled(this.value);
  73. resolvePromise(promise2,x,resolve,reject);
  74. }catch(e){
  75. reject(e);
  76. }
  77. },0)
  78. })
  79. this.onRejectedCallbacks.push(() => {
  80. setTimeout(() => {
  81. try{
  82. let x = onRejected(this.reason);
  83. resolvePromise(promise2,x,resolve,reject)
  84. }catch(e){
  85. reject(e)
  86. }
  87. },0)
  88. })
  89. })
  90. // console.log('pedding then...', this.value, this.reason)
  91. }
  92. return promise2
  93. }

catch方法

  1. catch(onRejected) {
  2. return this.then(null, onRejected)
  3. }

all, race,resolve, reject实现

  1. // 只要有一个promise成功了 就算成功。如果第一个失败了就失败了
  2. Promise.race = function (promises) {
  3. return new Promise((resolve, reject) => {
  4. for (var i = 0; i < promises.length; i++) {
  5. promises[i].then(resolve,reject)
  6. }
  7. })
  8. }
  9. // 生成一个成功的promise
  10. Promise.resolve = function(value){
  11. return new Promise((resolve,reject) => resolve(value)
  12. }
  13. // 生成一个失败的promise
  14. Promise.reject = function(reason){
  15. return new Promise((resolve,reject) => reject(reason))
  16. }