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)
})
那输出顺序是什么呢?
- // 同步 go…
- // promise go…
- // 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的失败呢?
- 捕获上面then的错误
- 上一个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呢?初学者的小伙伴自己搞一搞呢? 大神的话也不用我来说喽。
实现不一定多好,欢迎大神们来拍砖。。。
闪人!!!!