深入理解es6 promise(模拟promise)

26 篇文章 0 订阅
20 篇文章 1 订阅

es6的promise可以说是es6中最重要的语法之一,很多人都说是解决回掉地狱的很好的方法,其实它还是没有逃过回掉。

语法糖?

promise是语法糖吗?用代码告诉你:

setTimeout(function() {
	console.log('setTimeout go...')
})
var oP = new Promise(function (res, rej) {
    res('promise go...')
    console.log('同步 go...')
})

oP.then(res => {
    console.log(res)
})

那输出顺序是什么呢?

  1. // 同步 go…
  2. // promise go…
  3. // setTimeout go…
    很明显喽promise并不是简单的语法糖而是内置的构造函数,还有一些内置的东西。先来了解它的使用方法才能深入了解。
成功与失败的状态

首先呢promise必须使用new的方式调用,调用时必须传入一个函数为参数,此函数为同步调用,此函数可接受两个参数,第一个参数被调用了会触发成功状态,第二个被调用触发失败状态。
详见代码:

var oP = Promise(function(res, rej) {
    setTimeout(function() {
        if(Math.random() > 0.5) {
                res('成功');
            }else {
                rej('失败');
            }
    }, 1000);
})
// promise的重点就是解决异步问题,
// 所谓异步同步化
oP.then(function(res) {
    // 当成功状态被触发 调用此函数, 成功函数传入的参数就是 此函数的参数res
    console.log(res); //成功
}, function(err) {
    // 当失败状态被触发 调用此函数, 失败函数传入的参数就是 此函数的参数err
    console.log(err); //失败
});
then链式调用

promise的then函数可以链式调用,当第一个then调用后无论是成功还是失败触发的都是下一个then的成功回掉。

var oP = Promise(function(res, rej) {
       setTimeout(function() {
            if(Math.random() > 0.5) {
                    res('成功');
                }else {
                    rej('失败');
                }
        }, 1000);
});
oP
.then(function(res) {
    console.log(res)
    return '第一个成功'
}, function(err) {
    console.log(err)
    return '第一个失败'
})
.then(function(res) {
    // 无论第一个then成功还是失败 都会触发这个函数, 参数res为上一个then的返回值
    console.log(res) //第一个成功 or 第一个失败
}, function(err) {
    
})

那什么情况才会触发下面then的失败呢?

  1. 捕获上面then的错误
  2. 上一个then的返回值是promise对象,此proimse的状态是失败
var oP = Promise(function(res, rej) {
      setTimeout(function() {
           if(Math.random() > 0.5) {
                   res('成功');
               }else {
                   rej('失败');
               }
       }, 1000);
});
oP
.then(function() {
   throw new Error('下面捕获');
}, function() {
   throw new Error('下面捕获');
})
.then(function() {
   
}, function(err) {
   console.log(err); // Error : 下面捕获
});

// 当你想单独捕获错误也可以使用catch函数

oP
.then(function() {
   throw new Error('下面捕获');
}, function() {
   
})
.catch(function(err) {
   console.log(err) // Error : 下面捕获
});

// 当返回值是 promise对象时, 后面的then函数的运行情况取决于此promise的状态
oP
.then(function(data) {
   return new Promise(function (res, rej) {
       res(data);
   });
}, function(err) {
   return new Promise(function (res, rej) {
       rej(err);
   });
})
.then(function(res) {
   console.log(res); // 成功
}, function(err) {
   console.log(err); // 失败
});
空then, 静态函数

当then函数链式调用时,中间的空then会被忽略

var oP = Promise(function(res, rej) {
    res('成功');
});
oP
.then(function(res) {
    return res
})
.then()
.then(function(res) {
   console.log(res); //成功 
});

oP
.then(function(res) {
    throw new Error('捕获到错误')
})
.then()
.then(function(res) {  
}, function(err) {
    console.log(err); // Error: 捕获到错误
});

Promis还有两个静态方法,all,race
all方法接受一个promise数组,之后掉用then方法,当所有promise都成功时返回promise的返回值数组,当有任何一个promise失败时返回该promise的错误返回值。

function createProArr(num) {
    return new Promise(function(res, rej) {
       if(num > 6) {
           res(num);
       } else {
           rej('失败' + num);
       }
    });
}

Promise.all([createProArr(7), createProArr(8), createProArr(10)]).then(function(res) {
    console.log(res); // [7, 8, 10]
});

Promise.all([createProArr(7), createProArr(5), createProArr(10)]).then(function(res) {}, function(err) {
    console.log(err); // 失败5
});

race方法和all一样接受一个promise数组,之后调用then方法,当数组中任何一个promise状态发生变化时,then方法会捕获到该状态。

function createProArr(num, time) {
    return new Promise(function(res, rej) {
      setTimeout(function() {
           if(num > 6) {
                     res(num);
                 } else {
                     rej('失败' + num);
                 }
      }, time);
    });
}
Promise.all([createProArr(7, 1000), createProArr(8, 900), createProArr(10, 1100)]).then(function(res) {
    console.log(res); // 8
});
Promise.all([createProArr(7, 1000), createProArr(5, 600), createProArr(10, 900)]).then(function(res) {}, function(err) {
    console.log(err); // 失败5
});

ok,promise的使用大概就是这样,但是呢以我学啥都要学到细碎的性格咱模拟一个promise。

MyPromis

咱就用es5模拟,用es6模拟es6有点奇怪

失败与成功的状态与异步

其实promise有点想状态机,根据其状态的改变来触发then方法中的函数调用。

// promise三种状态 pending resolved rejected
function MyPromis(fn) {
     // 参数必须是一个函数
    if (typeof fn !== 'function') {
        throw Error(`Promise resolver ${fn} is not a function`);
    }
    
    var _self = this;        // 将this赋值给_self以备后用
    this.status = 'pending'; // 当前promise状态
    this.data = null;        // 回掉函数返回值储存
    
    function resolved(data) {
        if (_self.status === 'pending') {
            _self.status = 'resolved';
            _self.data = data;
        }
    }

    function rejected(err) {
        if (_self.status === 'pending') {
            _self.status = 'rejected';
            _self.data = err;
        }
    }
}
// then函数
MyPromise.prototype.then = function (onResolved, onRejected) {
        var _self = this;
        if (_self.status === 'resolved') {
            onResolved(_self.data)
        }
        if (_self.status === 'rejected') {
            onRejected(_self.data)
        }
}

ok,这个时候我们的promise可以最基本的调用了,但是呢还有很多问题,我们一个一个来解决。
首先异步的情况,
异步呢就在状态为pending时把失败函数与成功函数push进数组里,在成功和失败时便利数组调用

function MyPromis(fn) {
     // 参数必须是一个函数
    if (typeof fn !== 'function') {
        throw Error(`Promise resolver ${fn} is not a function`);
    }
    
    var _self = this;        // 将this赋值给_self以备后用
    this.status = 'pending'; // 当前promise状态
    this.data = null;        // 回掉函数返回值储存
    this.resolvedArr = [];   // 注册的成功的回掉函数
    this.rejectedArr = [];   // 注册的失败的回掉韩式
    
    function resolved(data) {
        if (_self.status === 'pending') {
            _self.status = 'resolved';
            _self.data = data;
            _self.resolvedArr.forEach(function (item) {
                item();
            });
        }
    }

    function rejected(err) {
        if (_self.status === 'pending') {
            _self.status = 'rejected';
            _self.data = err;
            _self.rejectedArr.forEach(function (item) {
                item();
            });
        }
    }
}
// then函数
MyPromise.prototype.then = function (onResolved, onRejected) {
        var _self = this;
        if (_self.status === 'resolved') {
            onResolved(_self.data)
        }
        if (_self.status === 'rejected') {
            onRejected(_self.data)
        }
        if (_self.status === 'pending') {
            _self.resolvedArr.push(function () {
                onResolved(_self.data)
            });
            _self.rejectedArr.push(function () {
                onRejected(_self.data)
            });
        }
}
then的链式与空then, 错误捕获

怎么才能链式调用呢,想想jquery就是返回本身喽,

 function MyPromise(fn) {
        // 参数必须是一个函数
        if (typeof fn !== 'function') {
            throw Error(`Promise resolver ${fn} is not a function`);
        }

        var _self = this;
        this.status = 'pending'; // 当前promise状态
        this.data = null;        // 回掉函数返回值储存
        this.resolvedArr = [];   // 注册的成功的回掉函数
        this.rejectedArr = [];   // 注册的失败的回掉韩式

        function resolved(data) {
            if (_self.status === 'pending') {
                _self.status = 'resolved';
                _self.data = data;
                _self.resolvedArr.forEach(function (item) {
                    item();
                });
            }
        }

        function rejected(err) {
            if (_self.status === 'pending') {

                _self.status = 'rejected';
                _self.data = err;
                _self.rejectedArr.forEach(function (item) {
                    item();
                });
            }
        }

        try {
            fn(resolved, rejected);
        } catch (e) {
            rejected(e);
        }
    }
    MyPromise.prototype.then = function (onResolved, onRejected) {
        var _self = this;
        // 空then 就是在then没有传入onResolved onRejected时我们自己处理一下
        if(!onResolved) {
            onResolved = function(res) {
                return res;
            }
        }
        if(!onRejected) {
            onRejected = function(err) {
                throw new Error(err);
            }
        }
        // 链式调用 我们返回一个新的promise对象 并把上一个then中的返回值 传给下一个then的成功函数
        // 用try catch捕获错误
        var promise = new MyPromise(function (res, rej) {
            if (_self.status === 'resolved') {
                setTimeout(function () {
                    try {
                        var nextProVal = onResolved(_self.data);
                        onResolved(nextProVal);
                    } catch (e) {
                        rej(e)
                    }
                });
            }
            if (_self.status === 'rejected') {
                setTimeout(function () {
                    try {
                        var nextProVal = onRejected(_self.data);
                        onResolved(nextProVal);
                    } catch (e) {
                        rej(e);
                    }
                });
            }
            if (_self.status === 'pending') {
                _self.resolvedArr.push(function () {
                    setTimeout(function () {
                        try {
                            var nextProVal = onResolved(_self.data);
                            onResolved(nextProVal);
                        } catch (e) {
                            rej(e);
                        }
                    });
                });
                _self.rejectedArr.push(function () {
                    setTimeout(function () {
                        try {
                            var nextProVal = onRejected(_self.data);
                            onResolved(nextProVal);
                        } catch (e) {
                            rej(e);
                        }
                    });
                });
            }
        });
        return promise
    }
返回 promise 对象
 function MyPromise(fn) {
        // 参数必须是一个函数
        if (typeof fn !== 'function') {
            throw Error(`Promise resolver ${fn} is not a function`);
        }

        var _self = this;
        this.status = 'pending'; // 当前promise状态
        this.data = null;        // 回掉函数返回值储存
        this.resolvedArr = [];   // 注册的成功的回掉函数
        this.rejectedArr = [];   // 注册的失败的回掉韩式

        function resolved(data) {
            if (_self.status === 'pending') {
                _self.status = 'resolved';
                _self.data = data;
                _self.resolvedArr.forEach(function (item) {
                    item();
                });
            }
        }

        function rejected(err) {
            if (_self.status === 'pending') {
                _self.status = 'rejected';
                _self.data = err;
                _self.rejectedArr.forEach(function (item) {
                    item();
                });
            }
        }

        try {
            fn(resolved, rejected);
        } catch (e) {
            rejected(e);
        }
    }
    // 这个函数用来处理上个then返回值当返回值是promise对象时我们就用我们返回的promise的res,rej来改变peomise状态
    // 品, 细品吧  我说不太明白
    function handleResolve(nextProVal, res, rej) {
        if (nextProVal instanceof MyPromise) {
            nextProVal.then(function(val) {
                res(val)
            }, function(err) {
                rej(err)
            })
        }else {
            res(nextProVal)
        }
    }
    
    MyPromise.prototype.then = function (onResolved, onRejected) {
        var _self = this;
        if(!onResolved) {
            onResolved = function(res) {
                return res
            }
        }
        if(!onRejected) {
            onRejected = function(err) {
                throw new Error(err)
            }
        }
        var promise = new MyPromise(function (res, rej) {
            if (_self.status === 'resolved') {
                setTimeout(function () {
                    try {
                        var nextProVal = onResolved(_self.data);
                        handleResolve(nextProVal, res, rej)
                    } catch (e) {
                        rej(e)
                    }
                });
            }
            if (_self.status === 'rejected') {
                setTimeout(function () {
                    try {
                        var nextProVal = onRejected(_self.data);
                        handleResolve(nextProVal, res, rej)
                    } catch (e) {
                        rej(e)
                    }
                });
            }
            if (_self.status === 'pending') {
                _self.resolvedArr.push(function () {
                    setTimeout(function () {
                        try {
                            var nextProVal = onResolved(_self.data);
                            handleResolve(nextProVal, res, rej)
                        } catch (e) {
                            rej(e)
                        }
                    });
                });
                _self.rejectedArr.push(function () {
                    setTimeout(function () {
                        try {
                            var nextProVal = onRejected(_self.data);
                            handleResolve(nextProVal, res, rej)
                        } catch (e) {
                            rej(e)
                        }
                    });
                });
            }
        });
        return promise
    }

上面就是完整的模拟peomise代码了,哎彩蛋来一波

all
 MyPromise.all = function (promiseList) {
        return new MyPromise(function (res, rej) {
            let resolveArr = [];
            promiseList.forEach((item, index) => {
                item.then(data => {
                    resolveArr.push(data);
                    if(promiseList.length === resolveArr.length) {
                        res(resolveArr);
                    }
                }, err => {
                    rej(err);
                });
            });
        });
    }

race呢?初学者的小伙伴自己搞一搞呢? 大神的话也不用我来说喽。
实现不一定多好,欢迎大神们来拍砖。。。
闪人!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值