对于promise相信很多人并不陌生,开发的过程中或多或少的都用到过,今天我们先不谈源码,看下promise的真面目到底是什么?
随便拉一个控制台打印一下,你会看到下面一长串的东西,这就是promise
这是一个构造函数,函数上有all, race, reject, resolve方法。原型上面有catch,then,方法。
如果你熟悉构造函数,你就能看的明白,函数上面的方法是这样来调用的,Promise.resolve(),原型上的方法是每一个实例都可以调用的,也就是说,new Promise().then().catch()。至于它为什么可以链式调用,应该是原型上的这些返回的是this,也就是每一个实例吧。今日不谈源码,所以这里不做过多的解释。
下面是一个简单的异步任务封装为同步任务的实现。
function fun1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('异步任务1')
}, 2000)
})
}
fun1().then((res)=>{
console.log(res) // 异步任务1
})
我们执行了一个异步,2秒后我们会拿到异步返回的结果,接下来我们可以做任何我们想要的操作。相比callback来说,这样看起更简介些。
如果遇到层层嵌套,一直用callbakc就会形成callback地狱。用promise你将会看到更优雅的解决办法。
function fun1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('异步任务1')
})
})
}
function fun2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('异步任务2')
})
})
}
function fun3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('异步任务3')
})
})
}
fun1().then((res1)=>{
console.log(res1)
return fun2()
}).then((res2) => {
console.log(res2)
return fun3()
}).then((res3) => {
console.log(res3)
})
保证了3个任务按照顺序执行。
return的结果不一定是一个promise实例,也可以是直接的数值。
fun1().then((res1)=>{
console.log(res1)
return '1'
}).then((res2) => {
console.log(res2)
return '2'
}).then((res3) => {
console.log(res3)
})
resolve的值是一个promise
function fun1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(fun2)
})
})
}
function fun2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(fun3)
})
})
}
function fun3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('异步任务3')
})
})
}
fun1().then((res1)=>{
console.log(res1) // 函数 fun2
}).then((res2) => {
console.log(res2) // undefined
}).then((res3) => {
console.log(res3) // undefined
})
这样看来,Promise不但没有那么繁琐,也很灵活。
catch的使用
function randomNumber(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10); //生成1-10的随机数
if(num<=5){
resolve(num);
}
else{
reject('数字太大了');
}
}, 2000);
});
return p;
}
randomNumber()
.then(
function(data){
console.log('resolved', data);
console.log(data);
})
.catch(
function(reason){
console.log('rejected', reason);
});
或者
也可以这么写
randomNumber()
.then(
function(data){
console.log('resolved', data);
}
,
function(reason){
console.log('rejected', reason);
}
);
效果一样。
有时候,我们会见到这样的写法,Promise.resolve(),或者Promise.reject(),这又是什么意思呢?直接看代码吧。
Promise.reject('请求异常').then((res1) => {
// 这里没有执行
console.log(res1)
}, (error) => {
// 这里执行了
console.log(error)
})
Promise.reject()
方法返回一个带有拒绝原因的Promise
对象。
Promise.resolve()
Promise.resolve('请求成功').then((res1) => {
// 这里执行了
console.log(res1)
}, (error) => {
// 这里没有执行
console.log(error)
})
如果说reslove的是一个promise呢?返回的将是带有结果的promise的本身。主要看打印日志的顺序。
var original = Promise.resolve(33);
var cast = Promise.resolve(original);
cast.then(function(value) {
console.log('value: ' + value);
});
console.log('original === cast ? ' + (original === cast));
返回一个带有then方法的对象,下面是MDN上面的例子,很好理解。
// Resolve一个thenable对象
var p1 = Promise.resolve({
then: function(onFulfill, onReject) { onFulfill("fulfilled!"); }
});
console.log(p1 instanceof Promise) // true, 这是一个Promise对象
p1.then(function(v) {
console.log(v); // 输出"fulfilled!"
}, function(e) {
// 不会被调用
});
// Thenable在callback之前抛出异常
// Promise rejects
var thenable = { then: function(resolve) {
throw new TypeError("Throwing");
resolve("Resolving");
}};
var p2 = Promise.resolve(thenable);
p2.then(function(v) {
// 不会被调用
}, function(e) {
console.log(e); // TypeError: Throwing
});
// Thenable在callback之后抛出异常
// Promise resolves
var thenable = { then: function(resolve) {
resolve("Resolving");
throw new TypeError("Throwing");
}};
var p3 = Promise.resolve(thenable);
p3.then(function(v) {
console.log(v); // 输出"Resolving"
}, function(e) {
// 不会被调用
});
Promise.all()。
先来看下MDN上对这个方法的解释吧:
Promise.all(iterable)
方法返回一个 Promise
实例,此实例在 iterable
参数内所有的 promise
都“完成(resolved)”或参数中不包含 promise
时回调完成(resolve);如果参数中 promise
有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise
的结果。
此实例在 iterable
参数内所有的 promise
都“完成(resolved):
var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
}).catch((error) => {
console.log(error)
});
”或参数中不包含 promise
:
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
}).catch((error) => {
console.log(error)
});
Promise.all([1, 2, 3]).then(function(values) {
console.log(values);
}).catch((error) => {
console.log(error)
});
如果参数中包含非 promise
值,这些值将被忽略,但仍然会被放在返回数组中(如果 promise
完成的话)
参数中 promise
有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise
的结果:
var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
setTimeout(reject, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
}).catch((error) => {
console.log(error) // foo
});
整个实例失败。Promise.all()会快速返回失败。
注意一下非 promise
值打印顺序,很奇怪,下面的promise怎么先执行了呢?会不会是当传递的参数不是promise的时候,就变成同步了呢?
我们来验证一下:
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
}).catch((error) => {
console.log(error)
});
Promise.all([1, 2, 3]).then(function(values) {
console.log(values);
}).catch((error) => {
console.log(error)
});
Promise.all([]).then(function(values) {
console.log(values);
}).catch((error) => {
console.log(error)
});
好像跟我们想的又不太一样,如果说参数中都是非promise的值,会变成同步任务的话,那么应该说先打印[1,2,3]后打印[]。结果却是相反的,这到底是怎么一回事呢?
var p = Promise.all([1, 2, 3]);
console.log(p)
setTimeout(() => {
console.log(p)
}, 2000)
这里注意,立即打印p还么有出结果,说明p是异步的,2秒后打印的p结果已经出来了。
var p1 = Promise.all([]);
console.log(p1)
setTimeout(() => {
console.log(p1)
}, 2000
这里立即打印p1结果就已经出来了,跟2秒后打印的是一样的,这就说明这里的p1是同步的。
上面的两组看到有什么不同了吗?
也就是说,如果参数都是非promise,那么仅当,参数是空数组([])的时候,才会变成同步的。
Promise.race()
race
函数返回一个 Promise
,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。
这个方法平时我们很少遇到,但是确实一个有很用的方法。话不多说,直接看代码吧。
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, "later");
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "fast");
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "fast"
// 两个都完成,但 p2 更快
});
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, "later");
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(reject, 100, "fast");
});
Promise.race([p1, p2]).then(function(value) {
// 这里没有走
console.log('resolve', value);
}).catch((error) => {
console.log('reject', error);
});
如果传的迭代是空的,则返回的 promise 将永远等待。
var p = Promise.race([]).then(function(value) {
// 这里没有走
console.log('resolve', value);
}).catch((error) => {
console.log('reject', error);
});
setTimeout(() => {
console.log(p)
}, 2000)
一直等待中。
如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race
将解析为迭代中找到的第一个值。
Promise.race([Promise.resolve(33), Promise.resolve(44)]).then(function(value) {
console.log('resolve', value);
}).catch((error) => {
console.log('reject', error);
});