promise “约不约”

1.promise 是什么鬼?

(小姐姐表示之前在项目中用promise时只是机械的照抄,完全不懂原理)

promise到底是个啥呢?是一个类?对象?数组?函数?废话不多说,打印出来看看

噢.....Promise是一个构造函数,自己身上有all、reject、resolve等方法,原型上有then、catch等方法。
为什么会出现promise呢?

promise是异步编程的一种解决方案。
promise主要用来解决两个问题:1.回调地域 2.可以支持多个并发的请求,获取并发请求中的数据
另外promise 有三种状态 pending->等待态   resolve->成功态    reject->失败态   状态一旦改变就不会再变

接下来 我们就深度解析下promise(小板凳摆好哟!!)
先new一个promise来看看

let p =new Promise1((resolve,reject)=>{
     setTimeout(()=>{
        resolve('成功');
    },1000)
})
p.then(data=>{
    return new Promise((resolve,reject)=>{
        resolve(1000)
    })
},err=>{
    console.log(err)
}).then(data=>{
    console.log(data);
})
复制代码

从这个例子中我们可以看到:

  • .new Promise时需要传递一个executor执行器,执行器会立刻执行
  • .执行器中传递了两个参数,resolve成功的函数(或者reject失败的函数) 他调用时可以传一个值 值可以是任何值
  • 只能从pending态到成功或者失败
  • promise 实例 每个实例都有一个then方法 这个方法传递两个参数 一个是成功一个是失败
  • 如果调用then时 发现已经成功了 会让成功函数执行并且把成功的内容当作参数传递到函数中 了解了这些就让我们动起来吧,啦啦啦啦啦......

2.根据promiseA+实现一个自己的promise

上面已经说了promise是一个构造函数,写起来...
function Promise(executor){
    let self=this;
    self.status='pending';
    self.value=undefined; //成功回调的值
    self.reason=undefined;//失败的原因
    self.onResolved=[]; //存放成功的回调 (异步操作时需要先将成功/失败的结果先存起来)
    self.onRejected=[]; //存放失败的回调
    function resolve(value){ //成功的回调
        if(self.status=='pending'){
            self.value=value;
            self.status='resolved';
            self.onResolved.forEach(fn=>fn());
        }     
    }
    function reject(reason){ //失败的回调
        if(self.status=='pending'){
            self.reason=reason;
            self.status='rejected';
            self.onRejected.forEach(fn=>fn());
        }      
    }
    try {
        executor(resolve,reject) // 执行器执行时需要捕获异常
    } catch (e) {
        reject(e)
    }
}
复制代码
构造函数写好了,接下来then方法链式调用
function  resolvePromise(promise2,x,resolve,reject){
    if(promise2===x){
        return reject(new TypeError('循环引用'));
    }
    //这个方法是处理所有promise的实现
    let called;  //用来防止多次调用
    
    // x是除了null以外的对象或者函数
    if (x != null && (typeof x === 'object' || typeof x === 'function')) {
        try{
            let then = x.then;
            if(typeof then ==='function'){
                //让返回的x也就是返回的promise执行 用它的状态让promise2成功或者失败
             //因为这里还涉及到别人写的promise 有的人写的promise 会成功还会调用失败
                then.call(x, (y)=>{ //如果Y是promise就继续递归promise
                    //递归解析 如果resolve返回的是一个promise就要不停的让resolve的结果进行处理
                    if (called) return ;
                    called=true;
                    resolvePromise(promise2, y, resolve, reject);
                },(e)=>{
                    if (called) return ;
                    called=true;
                    reject(e)  //只要失败了就失败了
                });
            }else {
                resolve(x)
            }

        }catch(e){
            if (called) return ;
            called=true;
            reject(e)
        } 
    }else {
        resolve(x) //x就是一个普通值 (就用这个值让返回的promise2成功即可)
    }
}
Promise.prototype.then = function (onfulfilled, onrejected) {
    // onfulfilled / onrejected是一个可选的参数
    onfulfilled = typeof onfulfilled == 'function' ? onfulfilled :  val=>val;
    onrejected = typeof onrejected === 'function' ? onrejected :err => {
      throw err;
    }
    let self = this;
    // 需要判断onfulfilled/onrejected执行的结果和promise2的关系
    // 
    let promise2;
    promise2 = new Promise((resolve, reject) => {
      if (self.status === 'resolved') {
        setTimeout(() => {
          try {
            let x = onfulfilled(self.value); //如果成功 返回成功的回调
            resolvePromise(promise2, x, resolve, reject); // 解析x 和 promise2的关系
          } catch (e) {
            reject(e);
          }
        }, 0);
      }
      if (self.status === 'rejected') {
        setTimeout(() => {
          try {
            let x = onrejected(self.reason); //如果失败 返回失败的回调
            resolvePromise(promise2, x, resolve, reject); // 解析x 和 promise2的关系
          } catch (e) {
            reject(e);
          }
        }, 0);
      }
      if (self.status === 'pending') {  //如果一直是pending态需要先把成功/失败的回调存起来等到成功或者失败状态时再执行
        self.onResolved.push(function () {
          setTimeout(() => {
            try {
              let x = onfulfilled(self.value);
              resolvePromise(promise2, x, resolve, reject); // 解析x(成功/失败的返回值) 和 promise2的关系
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        self.onRejected.push(function () {
          setTimeout(() => { 
            try {
              let x = onrejected(self.reason);
              resolvePromise(promise2, x, resolve, reject); // 解析x 和 promise2的关系
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    })
    return promise2;
}
复制代码

这一段有点儿长,额。。。。

  • 调用then时会传入两个参数(可选)成功的回调跟失败的回调 then方法想要链式下去那么得返回一个promise才可以继续链式
  • promise中可以同一个实例then多次,如果是pending状态 需要把成功或者失败的结果先存起来 等到状态确定后 再依次将对应的函数执行
  • then方法resolve返回的值如果是普通值,那么直接传递,如果resolve返回的是一个promise就要不停的让resolve的结果进行处理

核心点就是这些,代码中也加了注释,不知道大家懂了没,感觉有点儿乱

说下catch方法,它是做什么用的呢?其实它和then的第二个参数一样,用来指定reject的回调。

所以很简单咯

Promise.prototype.catch=function(onrejected){
  return this.then(null,onrejected)
}
复制代码

实际应用就是这样啦

let p =new Promise((resolve,reject)=>{
    resolve(100);
})
//catch就是不写成功的回调的then方法
p.then(data=>{
    console.log(data);
}).then(data=>{
    throw Error()
}).catch(err=>{
    console.log(err)
})
复制代码
接下来说all方法

all方法的参数是一个数组 会按照数组的结果放到成功的回调里(只有全成功才成功)

Promise.all = function (promises) {
    //promises是一个promise的数组
    return new Promise(function (resolve, reject) {
        let arr = []; //arr是最终返回值的结果
        let i = 0; // 表示成功了多少次
        function processData(index, data) {
            arr[index] = data;
            if (++i === promises.length) {
                resolve(arr);
            }
        }
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(function (data) {
                processData(i, data)
            }, reject)
        }
    })
}

复制代码

实例写起来

Promise.all([fs.readFile('./atxt','utf8'),fs.readFile('./b.txt','utf8')]).then((data)=>{
  console.log(data)
})
复制代码
再接下来是race方法

race方法参数也是一个数组 会同时发起并发,但是以返回最快的结果为结果

 Promise.race=function(promises){
    return new Promise((resolve,reject)=>{
        for(let i =0;i<promises.length;i++){
            let p=promises[i];
            p.then(resolve,reject)
        }
       })
 }
复制代码

实例写起来

Promise.race([fs.readFile('./atxt','utf8'),fs.readFile('./b.txt','utf8')]).then((data)=>{
  console.log(data)
})
复制代码

写到这儿,我表示已经很累了。。。。

接着搬砖

promise的resolve reject方法

这个就很简单了 resolve方法 就是直接掉成功的回到 reject方法就是直接调失败的回调

Promise.resolve=function(value){
    return new Promise((resolve,reject)=>{
        resolve(value)
    })
}
Promise.reject=function(reason){
    return new Promise((resolve,reject)=>{
        reject(reason)
    })
}
复制代码

最后来一个测试代码是否符合a+ 规范

Promise.defer=Promise.deferred = function(){
    let dfd = {};
    dfd.promise=new Promise((resolve,reject)=>{
        dfd.resolve=resolve;
        dfd.reject=reject
    })
    return dfd;
}
复制代码

这段代码是promise的语法糖 可以很简单的将一个方法包装成promise实例

let Promise =require('./promise');
let fs=require('fs');
function read(url){
    //语法糖
    let dfd=Promise.defer();
    fs.readFile(url,'utf8',(err,data)=>{
        if(err) dfd.reject(err);
        dfd.resolve(data)
    })
    return dfd.promise;
}
read('./a.txt').then(data=>{
    console.log(data)
})
复制代码

如果想知道自己写的代码是否符合a+规范可以用一个插件来测试

 npm install promises-aplus-tests -g
 promises-aplus-tests 文件名 可以测试
复制代码

到此就写完了,回过头来发现大堆的代码 没办法表述真的太难

推荐几个实现promise的库吧 Q   bluebird    then    有兴趣的了解下吧

我要回家睡觉去了!!!!!!

转载于:https://juejin.im/post/5b879537e51d4538b2048f85

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值