谈谈Promise

270 阅读7分钟

什么是Prmoise

Promise 是异步编程的一种解决方案,比传统的解决方案—回调函数和事件—更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

Promise对象可以理解为一次执行的异步操作,使用promise对象之后可以使用一种链式调用的方式来组织代码;让代码更加的直观。也就是说,有了Promise对象,就可以将异步操作以同步的操作的流程表达出来,避免了层层嵌套的回调函数。

Promise对象的特点

1、对象的状态不受外界影响。

Promise对象代表一个异步操作,有三种状态

  • pending(执行中)
  • Resolved(成功,又称Fulfilled)
  • rejected(拒绝)

其中pending为初始状态,fulfilled和rejected为结束状态(结束状态表示promise的生命周期已结束)。

promise只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

2、一旦状态改变,就不会再变,任何时候都可以得到这个结果。

Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected,只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果

Promise对象的缺点:

1、无法取消Promise,一旦新建它就会立即执行,无法中途取消。

2、如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

3、当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise更好的选择。

Promise的使用

1、基本用法:

(1)先new一个Promise,将Promise实例化

(2)然后在实例化的promise传两个参数,一个是成功之后的resolve,一个是失败之后的reject

(3)Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

var promise = function(isReady){
    return new Promise(function(resolve, reject){
        // do somthing, maybe async
        if (isReady){
          return resolve('hello world');
        } else {
          return reject('failure');
        }
    });
}
 
//Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。
promise(true).then(function(value){
    // success,这里是resolve的回调函数
    console.log(value);  //hello world
}, function(err){
    // failure,这里是reject的回调函数
    console.log(err)
})

2、链式调用

then()方法的作用是Promise实例添加解决(fulfillment)和拒绝(rejection)状态的回调函数。then()方法会返回一个新的Promise实例,所以then()方法后面可以继续跟另一个then()方法进行链式调用。

let p = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 'success');
});
p.then(
    res => {
        console.log(res);
        return new Promise((resolve, reject) => {
            setTimeout(resolve, 1000, 'success');
        });
    }
).then(
    res => console.log(res)
);
// success
// 1000ms后
// success

3、catch方法 其实它和then的第二个参数一样,用来指定reject的回调,不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。

getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
    console.log(someData); //此处的someData未定义
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);
});

如果我们不用Promise,代码运行到console就直接在控制台报错了,不往下运行了。但是在这里,进到catch方法里面去了,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了,这与我们的try/catch语句有相同的功能。

4、all的用法

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。Promise.all可以接收一个元素为 Promise 对象的数组作为参数,当这个数组里面所有的 Promise 对象都变为 resolve 时,该方法才会返回。(Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)

var p1 = new Promise(function (resolve) {
    setTimeout(function () {
        resolve("promise1");
    }, 2000);
});

var p2 = new Promise(function (resolve) {
    setTimeout(function () {
        resolve("promise2");
    }, 1000);
});

Promise.all([p1, p2]).then(function (result) {
    console.log(result); // ["promise1", "第二个promise2"]
});

5、race的用法

promise.race也是传入一个数组,但是与promise.all不同的是,race只返回跑的快的值,也就是说result返回比较快执行的那个。

var p1 = new Promise(function (resolve) {
    setTimeout(function () {
        console.log(1);
        resolve("promise1");
    }, 2000);
});

var p2 = new Promise(function (resolve) {
    setTimeout(function () {
        console.log(2);
        resolve("promise2");
    }, 1000);
});

Promise.race([p1, p2]).then(function (result) {
    console.log(result); 
});

// 结果:
// 2
// promise2
// 1

可以看到,传的值中,只有p2的返回了,但是p1没有停止,依然有执行。

race的应用场景为,比如我们可以设置为网路请求超时。写两个promise,如果在一定的时间内如果成功的那个我们没有执行到,我们就执行失败的那个.

手写Promise

手写实现Promise代码:

function resolvePromise(promise2,x,resolve,reject){
    //判断x是否等于promise
    //promiseA+规定了一段代码,可以实现promise之间的交互
    if(promise2 === x){//不能自己等待自己完成
        return reject(new TypeError('循环引用'))
    }
    if(x != null && (typeof x === 'object' || typeof x === 'function')){
        //不是null 也不是对象和函数
        let called;//防止成功后再调用失败
        try{//防止取then时报错
            let then = x.then;
            if(typeof then === 'function'){//如果是函数就认为它是promise
                //call第一参数是this,后面是成功的和失败的回调
                then.call(x,y=>{
                    if(called)return;
                    called = true;
                    resolvePromise(promise2,y,resolve,reject)//递归
                },r=>{
                    if(called)return;
                    called = true;
                    reject(r)
                })
            }else{//then是个普通对象 直接成功即可
                resolve(x)
            }
        }catch(e){
            if(called)return;
            called = true;
            reject(e)
        }
    }else{//x就是个普通值
        resolve()
    }
}
class Promise{
    constructor(executor){
        this.status = 'pending';//默认等待状态
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCb = [];//存放成功的回调
        this.onRejectedCb = [];//存放失败的回调
        let resolve = (data) => {
            if(this.status === 'pending'){
                this.value = data;
                this.status = 'resolved';
                this.onResolvedCb.forEach(fn=>fn());//解决异步问题
            }
        }
        let reject = (reason) => {
            if(this.status === 'pending'){
                this.reason = reason;
                this.status = 'rejected';
                this.onRejectedCb.forEach(fn=>fn());
            }
        }
        try{//执行时可能会发生异常
            executor(resolve,reject);
        }catch(e){
            reject(e)
        }
    }
    then(onFullFilled,onRejected){
        //then不传参的时候
        onFullFilled = typeof onFullFilled === 'function' ? onFullFilled : y => y;
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

        let promise2;//链式调用
        if(this.status === 'resolved'){
            // onFullFilled(this.value);
            promise2 = new Promise((resolve,reject)=>{
                try{
                    let x = onFullFilled(this.value);
                    //看x是不是promise 是promise或者是个普通值的话 作为promise2的成功结果  
                    resolvePromise(promise2,x,resolve,reject)        
                    //resolvePromise可以解析x和promise之间的关系   
                }catch(e){
                    reject(e)
                }
            })
            return promise2;//调用then后返回一个新的promise
        }
        if(this.status === 'rejected'){
            promise2 = new Promise((resolve,reject)=>{
                try{
                    let x = onFullFilled(this.reason);
                    resolvePromise(promise2,x,resolve,reject)  
                }catch(e){
                    reject(e)
                }
            })
            return promise2;
        }
        if(this.status === 'pending'){//既没有成功也没有失败(先执行了then)
            promise2 = new Promise((resolve,reject)=>{
                this.onResolvedCb.push(()=>{//存放成功回调
                    try{
                        //还可以做其他操作
                        let x = onFullFilled(this.value)
                        resolvePromise(promise2,x,resolve,reject)  
                    }catch(e){
                        reject(e)
                    }            
                })
                this.onRejectedCb.push(()=>{//存放失败回调
                    try{
                        //还可以做其他操作
                        let x = onRejected(this.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }             
                })
            })
            return promise2;
        }
    }
    catch(onRejected){
        return this.then(null,onRejected);
    }
}
Promise.resolve = function(val){
    return new Promise((resolve,reject)=>resolve(val))
}
Promise.reject = function(val){
    return new Promise((resolve,reject)=>reject(val))
}
Promise.all = function(promises){
    return new Promise((resolve,reject)=>{
        let arr=[],i=0;
        function processData(index,data){
            arr[index]=data;
            i++;
            if(i ===promises.length){
                resolve(arr)
            }
        }
        for(let i=0;i<promises.length;i++){
            promises[i].then(data=>{
                processData(i,data)
            },reject)
        }
    })
}
Promise.race = function(promises){
    return new Promise((resolve,reject)=>{
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(resolve,reject)
        }
    })
}
//promise 语法糖
Promise.deferred = Promise.defer = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve,reject)=>{
      dfd.resolve = resolve;
      dfd.reject = reject;
    })
    return dfd;
  }
  // npm install promises-aplus-tests -g
  // promises-aplus-test 文件名
module.exports = Promise;