This is my Promise

Promise专题

1.什么是Promise?

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

2.对于几种常见异步编程方案?(暂不说generator函数和async函数)

1.回调函数;
2.事件监听
3.发布/订阅模式
4.Promise对象

3.方案对比

这里为了突出Promise在异步编程的优势,我主要使用了回调函数Promise作出对比

1.回调函数

我们接触比较多的回调函数,我举个例子,我们们用Jquery的ajax获取数据时 都是以回调函数方式获取的数据

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

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

$.get(url, data1 => {
    console.log(data1)
    $.get(data1.url, data2 => {
        console.log(data1)
        $.get(data2.url, data3 => {
            console.log(data2)
              .....
        })
    })
})

这样一来,在处理越多的异步逻辑时,就需要越深的回调嵌套,导致代码书写顺序与执行顺序不一致,不利于阅读与维护,不利于bug的修复和新需求的加入等等。

2.Promise方案

这里我们使用Promise来解决这个callback-hell问题。

const asyncRequest = url => {
    return new Promise((resolve,reject)=>{
        $.get(url, data => {
            resolve(data)
        });
    })
}
首先对Jq的请求函数进行Promise封装


// 请求data1
asyncRequest(url)
.then(data1 => {
    return request(data1.url);   
})
.then(data2 => {
    return request(data2.url);
})
.then(data3 => {
    console.log(data3);
})
.catch(err => throw new Error(`error  of ${err} happen in asyncRequest function`));

经过Promise处理的Jq请求函数,在处理上述中回调地狱情形是如此优势巨大,整个请求下来,逻辑非常清晰和有利于代码的维护。


接下来为了更加深入地了解Promise,我们不如造一个属于自己的Promise吧,不过这个Promise不会完全实现和PromiseA+规范的Promise,这里我们只需要实现我们平时使用的Promise中的静态resolve,reject函数,原型对象的then函数,catch函数,实现链式调用,而race和all函数这些附加性质的函数,我们有时间再去实现,我们先简单实现上述目标!


等等,实现一个Promise之前,我们先了解Promise的特性,不然你不知道需求,写什么代码啊!!

3.Promise特性

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

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

2.Promise是一个状态机,有三种不同的状态。

  • pending
  • fulfilled
  • rejected

(1) promise 对象初始化状态为 pending
(2) 当调用resolve(成功),会由pending => fulfilled
(3) 当调用reject(失败),会由pending => rejected

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

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

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

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

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

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

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

(4) promise.catch
在链式写法中可以捕获前面then中发送的异常

promise.catch(onRejected)
相当于
promise.then(null, onRrejected);

// 注意
// onRejected 不能捕获当前onFulfilled中的异常
promise.then(onFulfilled, onRrejected); 

// 可以写成:
promise.then(onFulfilled)
       .catch(onRrejected); 

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

function taskA() {
    console.log("Task A");
}
function taskB() {
    console.log("Task B");
}
function onRejected(error) {
    console.log("Catch Error: A or B", error);
}

var promise = Promise.resolve();
promise
    .then(taskA)
    .then(taskB)
    .catch(onRejected) // 捕获前面then方法中的异常

(6)Promise的静态方法

  • Promise.resolve 返回一个fulfilled状态的promise对象
Promise.resolve('hello').then(function(value){
    console.log(value);
});

Promise.resolve('hello');
// 相当于
const promise = new Promise(resolve => {
   resolve('hello');
});

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

Promise.reject(24);
new Promise((resolve, reject) => {
   reject(24);
});

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

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

const p1 = new Promise((resolve, reject) => {
   resolve(1);
});

const p2 = new Promise((resolve, reject) => {
   setTimeout(()=>{
       resolve(2);
    },1000)
   });

const p3 = new Promise((resolve, reject) => {
   reject(3);
});

Promise.all([p1, p2, p3]).then(data => { 
   console.log(data); // [1, 2, 3] 结果顺序和promise实例数组顺序是一致的
}, err => {
   console.log(err);
});

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

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

function timerPromisefy(delay) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
Promise.race([
    timerPromisefy(10),
    timerPromisefy(20),
    timerPromisefy(30)
]).then(function (values) {
    console.log(values); // 10
});

Promise特征总结

1.Promise是一个构造函数,接受一个函数作为参数,而该函数两个函数类型形参resolve reject,这两函数是构造函数内部提供。
2.Promise有三种状态pending,fulfilled,rejected且状态不能逆转
3.Promise对象有三个方法then,resolve,reject,catch
4.Promise能够进行链式调用
5.Promise静态方法resolve,reject,all,rece。


需求准备好,开始早一个Promise!(all,race暂不实现,另外不对thenable对象,和内部可能存在的其他Promise的情形作处理,先通过数字字符串对象等情况)

go on

首先我们要将下面这个回调函数做做处理变成Promise形式

function doSomething(cb) {
  cb();
}

biubiubiu

function doSomething(){
    return {
        then(cb) {
            cb()
        }
    }
}

好得到这段毫无意义的代码,不过看上去好像有点样式...!我们目前还没有触及 Promise 背后的核心概念,但这也是个小小的开始。

好继续改造

上面代码:
function MyPromise(){
    return {
        then(cb) {
            cb()
        }
    }
}

改造代码:
function MyPromise(excutor){
    let callback = null;  
    this.then = function(cb) {
        callback = cb
    }
    function resolve(val) {      
        callback(val)
    }
    try{
        excutor(resolve)
    }catch(error){
       throw new Error(error)
    }

}

这里改造后的代码,好像有模有样了,但是这里就遇到一个问题:如果你逐行执行代码,就会发现resolve() 函数then() 函数之前被调用,这就意味着resolve() 被调用的时候,callback 还是 null。让我们用一个 hack 来干掉这个问题,引入setTimeout处理一下,代码如下所示:

function MyPromise(excutor){
    let callback = null;  
    this.then = function(cb) {
        callback = cb
    }
    function resolve(val) {      
        setTimeout(()=>{
           callback(val)
        },0)
    }
    try{
        excutor(resolve)
    }catch(error){
       throw new Error(error)
    }
}

测试代码:
console.log(0)
var a = new MyPromise((resolve,reject)=>{
    console.log(1);
    resolve(2);
})
a.then(val=>{
    console.log(val)
})
console.log(3)
结果:0,1,3,2

做到这里,程序的执行顺序和我们平常的Promise顺序一致唉!不过还存在非常多的问题!

  • 没有链式调用
  • 没有Promise状态
  • 没有reject函数作错误返回,catch函数做错误捕捉等等...

好继续改造

// promise 三个状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

//这里用用es6的语法
class MyPromise {
  constructor(excutor) {
      if(typeof excutor !== 'function') {
          throw new Error(`${excutor} is not a function`);
      }
      let that = this;
      this.state = PENDING//fulfilled reject
      this.value = undefined;
      this.reason = undefined;
      this.onFulfilledCallbacks = [];//纪录成功的回调,就是then的第一个参数
      this.onRejectedCallbacks = [];//纪录成功的回调,就是then的第二个参数

    function resolve(value) {
       setTimeout(()=>{
           that.state = FULFILLED;
           that.value =value;
           that.onFulfilledCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.value);
           })
        })
    }
    function reject(reason) {
         setTimeout(()=>{
            that.state  = REJECTED;
            that.reason = reason;
            that.onRejectedCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.reason);
              })
           },0)
     }

      try {
         excutor(resolve,reject);
      } catch (error) {
          reject(error);
      }
  }

  then(onFulfilled,onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason=>{
            throw new Error(reason)
        }
       return new MyPromise((resolve,reject)=>{
/*
*这里是箭头函数,所以这里this.onFulfilledCallbacks和*this.onRejectedCallbacks是指向外层的成功和失败回调数据,
*假如是function的形式,就需要提前定义这个**参数函数**然后通过*bind()函数绑定this的指向
*/            
        this.onFulfilledCallbacks.push(value)=>{
                try {
                    let newValue = onFulfilled(value);
                    resolve(newValue);
                 } catch (error) {
                    reject(error)
                }               
             }
              this.onRejectedCallbacks.push(value=>{
                  try {
                      let reason = onRejected(value);
                      reject(reason)
                  } catch (error) {
                      reject(error)
                  }
            })
       })
  }

}

测试代码:
console.log(0)
 var a = new SuperPromise((resolve,reject)=>{
     console.log(1)
     resolve(2)
 })

 a.then((val)=>{
     console.log(val)
     return (3)
 }).then(val=>{
     console.log(val)
 })
 console.log(4)
结果//0,1,4,2,3

好,这里我们实现了then的链式调用,这一看,我们只实现了原型上一个then函数,接下来我们继续实现catch,和静态resolve,静态reject

go on

上面我们提到catch函数:
promise.catch(onRejected)
相当于
promise.then(null, onRrejected);
但是有一点不同的是,onRrejected不是不能捕捉到onFulfilled中的异常。

class MyPromise {
  constructor(excutor) {
      if(typeof excutor !== 'function') {
          throw new Error(`${excutor} is not a function`);
      }
      let that = this;
      this.state = PENDING//fulfilled reject
      this.value = undefined;
      this.reason = undefined;
      this.onFulfilledCallbacks = [];//纪录成功的回调,就是then的第一个参数
      this.onRejectedCallbacks = [];//纪录成功的回调,就是then的第二个参数

    function resolve(value) {
       setTimeout(()=>{
           that.state = FULFILLED;
           that.value =value;
           that.onFulfilledCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.value);
           })
        })
    }
    function reject(reason) {
         setTimeout(()=>{
            that.state  = REJECTED;
            that.reason = reason;
            that.onRejectedCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.reason);
              })
           },0)
     }

      try {
         excutor(resolve,reject);
      } catch (error) {
          reject(error);
      }
  }

     then(onFulfilled,onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason=>{
            throw new Error(reason)
        }        
       if(this.state === PENDING) {
            return new MyPromise((resolve,reject)=>{
                this.onFulfilledCallbacks.push(value=>{
                    try {
                        let newValue = onFulfilled(value);
                        resolve(newValue);
                    } catch (error) {
                        reject(error)
                    }
                })

                this.onRejectedCallbacks.push(value=>{
                    try {
                        let newValue = onRejected(value);
                        reject(newValue)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
        }
     }

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

测试代码:
 var b = new MyPromise((resolve,reject)=>{
     reject('error happen in SuperPromise')
 })

 b.then((val)=>{
     console.log(val,'val')
     return val;
 })
 .catch(err=>{
     console.log(err)
 })


-------
 var c = new MyPromise((resolve,reject)=>{
     resolve(2)
 })

 c.then((val)=>{
     console.log(val,'val')
     throw new Error('some error in then')
 })
 .catch(err=>{
     console.log(err)
 })

接下黎啊继续实现静态resolve和reject

class MyPromise {
  constructor(excutor) {
      if(typeof excutor !== 'function') {
          throw new Error(`${excutor} is not a function`);
      }
      let that = this;
      this.state = PENDING//fulfilled reject
      this.value = undefined;
      this.reason = undefined;
      this.onFulfilledCallbacks = [];//纪录成功的回调,就是then的第一个参数
      this.onRejectedCallbacks = [];//纪录成功的回调,就是then的第二个参数

    function resolve(value) {
       setTimeout(()=>{
           that.state = FULFILLED;
           that.value =value;
           that.onFulfilledCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.value);
           })
        })
    }
    function reject(reason) {
         setTimeout(()=>{
            that.state  = REJECTED;
            that.reason = reason;
            that.onRejectedCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.reason);
              })
           },0)
     }

      try {
         excutor(resolve,reject);
      } catch (error) {
          reject(error);
      }
  }

     then(onFulfilled,onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason=>{
            throw new Error(reason)
        }        
       if(this.state === PENDING) {
            return new MyPromise((resolve,reject)=>{
                this.onFulfilledCallbacks.push(value=>{
                    try {
                        let newValue = onFulfilled(value);
                        resolve(newValue);
                    } catch (error) {
                        reject(error)
                    }
                })

                this.onRejectedCallbacks.push(value=>{
                    try {
                        let newValue = onRejected(value);
                        reject(newValue)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
        }
     }

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

//这两个就比较简单,我们回忆一下,当我们Promise.resolve(),就会马上进入成功回调,相反Promise.reject(),就马上进入失败回调

  static resolve(val){
      return new MyPromise((resolve,reject)=>{
            resolve(val)
      })
  }

  static reject(reason){
      return new MyPromise((resolve,reject)=>{
          reject(reason)
      })
   }
}

测试代码:
 MyPromise.resolve(2).then(val=>{
     console.log(val)
     return 3
 }).then(val=>{
     console.log(val)
 })

---
 MyPromise.reject('some error happen in static reject')
 .catch(err => console.log(err))

以上两段代码测试通过,完美!!!
好,我们再看看我们的需求

(all,race暂不实现,另外不对thenable对象,和内部可能存在的其他Promise的情形作处理,先通过数字,字符串,对象等情况)

OK这里已经实现的Promise已经实现了我们上述的请求,如果看客跟着我一起去实现一个Promise,是不是会有一些成就感呢?说笑,这节说到这里。good night

good night
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容