【ES6】Promise对象的详解与实现

Promise对象的详解与实现

参考资料:《ES6标准入门第3版》

目录

Promise对象的详解与实现

Promise 对象详解

1. 什么是Promise?

Promise 对象的三种状态

2.2 基本用法

2.3 Promise 的 then() 方法

2.4 Promise 的 catch() 方法

2.5 Promise.resolve() 和 Promise.reject() 把现有对象转为Promise对象

2.5.1 参数是一个Promise实例

2.5.2 参数是一个带有then方法的对象

2.5.3 参数不是具有then方法的对象或根本不是对象

2.5.4 不带任何参数

2.5.5 Promise.reject() 

2.6 Promise.all() 和 Promise.race()

Promise对象的实现


一 、Promise对象详解

1. 什么是Promise?

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

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

1.1 Promise对象的三种状态

Promise对象代表着一个异步操作,它有三种状态:pending(异步操作进行中)、fulfilled(异步操作已完成)、rejected(异步操作已经失败)

1.2 Promise对象的三个特点

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,至始至终会一直保持这个结果。

(3)Promise也有一些缺点。例如,无法取消Promise,一旦新建Promise对象它就会立即执行传入的函数,无法中途取消。

注意,为了方便理解(与 resolve 函数对应),统一使用 resolved 指代 fullfilled 状态。

2. 基本用法

ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。

下面代码创造了一个Promise实例。

注:文件读写是一个异步操作。

const fs = require('fs')

var promise =  new Promise(function (resolve, reject) {
        fs.readFile('./1.txt', 'utf-8', (err, data)=>{
            if(err)
                // 异步操作失败:文件读写失败 
                reject(err)
            else 
                // 异步操作成功:文件读写成功
                resolve(data)
        })
    })

2.1 新建Promise传入的参数

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去,参数也可不传。

reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去,参数也可不传。

2.2 then方法的作用

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数,Promise 实例对象的 .then 方法中接收两个参数,第一个参数是 Promise 对象由 pending 变成 resolved 时调用的回调函数 resolve,第二个参数是 Promise 对象由 pending 变成 rejected 状态的时候调用的回调函数 reject。

promise.then(function(val){
    // promise 执行成功的回调函数的内容
},function(err){
    // promise 执行失败的回调函数的内容
})

2.3 一个完整的例子

其中当前目录下有一个文件 1.txt 其中的内容是 “111”

const fs = require('fs')

var promise =  new Promise(function (resolve, reject) {
	 
        console.log("我被立即执行了")
        fs.readFile('./1.txt', 'utf-8', (err, data)=>{
            if(err)
                // 异步操作失败:文件读写失败 
                reject(err)
            else 
                // 异步操作成功:文件读写成功
                resolve(data)
        })
    })

promise.then(function(val){
    console.log(val)
},function(err){
    console.log(err)
})

在上述代码中,创建 promise 实例对象后,就会输出“我被立即执行了”。

在 promise 对象中文件的读取是异步操作,把它封装在 promise 对象当中,then 方法指定的成功或者失败时会执行的回调函数。

当readFile方法在执行的时候,由于是异步操作,结果不会立即返回,所以容器的状态是pending。当文件读取完成后,promise容器的状态由 pending 变为 resolved 或者 rejected,此时才会执行相应的回调函数。

这里文件读取成功的结果会输出 “111”(即执行 resolve 函数)若文件 1.txt 不存在则会报错,“Error: ENOENT: no such file or directory“(即执行 reject 函数)。

3. Promise 的 then() 方法与链式调用

Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

可以在 then 方法中传入的回调函数返回一个新的 Promise 实例,这样可以采用链式写法,在 then 方法后可以再调用另外一个 then 方法,同时采用这样写法的第二个 Promise 实例会等待第一个 Promise 实例执行完对象内的异步操作执行回调函数结束后才执行,这样就可以保证两个或者多个异步操作可以按序执行。

const fs = require('fs')

function readFile(file){
	return new Promise(function (resolve, reject) {
        fs.readFile(file, 'utf-8', (err, data)=>{
            if(err)
                reject(err)
            else 
                resolve(data)
        })
    })
}

readFile("1.txt").then(data => {
	console.log(data);
	return readFile("2.txt")
}).then(data=>{
	console.log(data)
})

// 111
// 222

4. Promise 的 catch() 方法

Promise 对象的 then 方法中可以不指定异步操作发生错误的 rejectd 状态下的回调函数,可以在一系列 Promise 对象链式调用的最后使用 catch 方法来捕捉 Promise 对象发生的错误。

readFile("1.txt").then(data => {
	
    console.log(data)
    return readFile("2.txt")

}).then(data=>{
	
    console.log(data)
    return readFile("3.txt")

}).then(data=>{
    
    console.log(data)

}).catch(err=>{
	
    console.log(err)

})
// catch 方法可以捕捉链式写法中所有 promise 对象发生的错误
// 一旦在链中有 promise 对象发生错误,该链条后的所有 then 方法不再执行,转向执行 catch 方法中的回调函数。

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

 

5. Promise.resolve() 和 Promise.reject() 方法

使用Promise原型对象中的 Promise.resolve() 方法可以把现有对象转为新的 Promise 对象,状态为resolve。

Promise.resolve('foo')

// 等价于

new Promise(resolve => resolve('foo'))

Promise.resolve() 方法的参数分为以下四种情况。

5.1 参数是一个Promise实例

如果参数是一个 Promise 实例,那么 Promise.resolve 将不做任何修改,原封不动地返回这个实例。

5.2 参数是一个带有then方法的对象

例如下面的thenObj:

var thenObj = {
    then: function(resolve, reject){
        resolve(1);
    }
}

此时Promise.resolve() 方法会将这个对象转为 Promise 对象,然后立即执行 thenObj 对象的 then 方法。

5.3 参数不是具有then方法的对象或根本不是对象

如果参数是一个原始值,或者是一个不具有then方法的对象,那么Promise.resolve方法返回一个新的Promise对象,状态为Resolved。

var p = Promise.resolve('Hello World');

p.then(function (s){
    console.log(s)
});

// 输出 Hello World

5.4 不带任何参数

Promise.resolve 方法允许在调用时不带有参数,而直接返回一个 Resolved 状态的 Promise 对象。

var p = Promise.resolve()

p.then(function(){
       // 要执行的代码
});

5.5 Promise.reject() 

Promise.reject() 方法也会立即返回一个新的 Promise 实例,状态为 rejected。

其余用法与Promise.resolve的四个用法一致。

6. Promise.all() 和 Promise.race()

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

// p1、p1、p3均为 promise 对象
const p = Promise.all([p1, p2, p3]);

上面代码中,Promise.all方法接受一个数组作为参数,p1p2p3都是 Promise 实例,如果不是,就会先调用上面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。(Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)

p的状态由p1p2p3决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1p2p3之中有一个实例率先改变状态(race竞赛),p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

Promise.race方法的参数与Promise.all方法一样,如果不是 Promise 实例,就会先调用上面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。

二、Promise对象的实现

大概理解了Promise 对象的使用方式,可以自己实现一个 Promise 来深入了解 Promise 对象的原理。

下面是我认为写的很好且容易读懂的一段代码,转载自https://blog.youkuaiyun.com/yangbo1993/article/details/79034868

另外一篇宝藏文章:https://www.jianshu.com/p/43de678e918a

/**
 * 实现一个Promise
 */
function Promise(task) {
    //接收一个处理函数
    let that = this;//缓存this
    //promise有三种状态  默认为pending
    that.status = 'pending';
    that.onFulfilledFns = [];//所有成功的回调
    that.onRejectedFns = [];//所有失败的回调
    that.value = undefined;
    function resolve(value) {
        //成功函数
        if(that.status == 'pending'){
            that.status = 'fulfilled';
            that.value = value;
            //执行所有成功的回调
            that.onFulfilledFns.forEach(item=>item(value));
        }
    };
    function reject(reason) {
        //失败函数
        if(that.status == 'pending'){
            that.status = 'rejected';
            that.value = reason;
            //执行所有失败的回到
            that.onRejectedFns.forEach(item=>item(reason));
        }
    };
    //立即执行传入的处理函数
    try{
        task(resolve,reject);
    }catch (err){
        reject(err)
    }
};
function resolvePromise(promise2,x,resolve,reject) {
    let then;
    if(promise2 === x){
        return reject(new Error('循环引用'));
    }
    if(x instanceof Promise){
        //判断x的prototype所指向的对象是否存在Promise的原型链上
        if(x.status= 'pending'){
            x.then(function (y) {
                //递归 调用
                resolvePromise(promise2,y,resolve,reject);
            },reject)
        }else if(x.status == 'fulfilled'){
            resolve(x.value);
        }else if(x.status == 'rejected'){
            reject(x.value);
        }
    }else if(x != null && typeof x == 'object' || typeof x == 'function'){
        try{
            then = x.then;
            if(typeof then == 'function'){
                then.call(x,function (y) {
                   resolvePromise(promise2,y,resolve,reject);
                },function (y) {
                    reject(y)
                });
            }
        }catch (e){
            reject(e);
        }
    }else{
        resolve(x);
    }
}
//then方法
Promise.prototype.then = function (onFulfilled, onRejected) {
    //假如没有传入异步处理程序则直接返回结果
    onFulfilled = typeof onFulfilled == 'function'?onFulfilled:function (value) {
        return value;
    };
    onRejected = typeof onRejected == 'function'?onRejected:function (reason) {
        return reason;
    };
    var promise2;//用来实现链式调用
    let that = this;
    if(that.status == 'fulfilled'){
        promise2 = new Promise(function (resolve,reject) {
            let x = onFulfilled(that.value);
            resolvePromise(promise2,x,resolve,reject);
        });
    }else if(that.status == 'rejected'){
        promise2 = new Promise(function (resolve,reject) {
            let x = onRejected(that.value);
            reject(x);
        });
    }else if(that.status == 'pending'){
        promise2 = new Promise(function (resolve,reject) {
            that.onFulfilledFns.push(function(){
                let x = onFulfilled(that.value);
                resolve(x);
            });
            that.onRejectedFns.push(function () {
                let x = onRejected(that.value);
                reject(x);
            });
        });
    }else{
        promise2 = new Promise(function (resolve,reject) {
            reject('Promise内部状态错误');
        });
    }
    return promise2;
};
Promise.resolve = function (val) {
    return new Promise(function (resolve,reject) {
        resolve(val);
    });
};
Promise.reject = function (val) {
    return new Promise(function (resolve,reject) {
        reject(val);
    });
};
Promise.all = function (arrs) {
    //all方法接收一个promise数组,数组中所有异步操作结束后返回一个新的promise
    if(typeof arrs == 'object' && arrs.length > 0){
        return new Promise(function (resolve,reject) {
            let result = [];//新的promise返回结果
            let indexNum = 0;//当前完成几个
            let resolved = function (index) {
                return function (data) {
                    result[index] = data;
                    indexNum++;
                    if(indexNum == arrs.length){
                        resolve(result);
                    }
                }
            };
            for(let i=0;i<arrs.length;i++){
                arrs[i].then(resolved(i),function (reason) {
                    reject(reason);
                });
            };
        });
    }else{
        return new Promise(function (resolve,reject) {
            reject(new Error('all方法传入参数错误'));
        });
    }
};
Promise.race = function (arrs) {
    if(typeof arrs == 'object' && arrs.length > 0){
        return new Promise(function (resolve,reject) {
            for(let i=0;i<arrs.length;i++){
                arrs[i].then(function (data) {
                    resolve(data);
                },function (err) {
                    reject(err);
                });
            };
        });
    }else{
      return new Promise(function (resolve,reject) {
          reject(new Error('race方法传入参数错误'));
      })
    };
};
 
module.exports = Promise;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值