个人的第一感觉:Promise可以将异步函数中的执行结果回传到主线程上
实际上Promise的作用就是把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
Promise的构造函数接收一个作为参数的函数,
这个函数传入了两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。
resolve是将Promise的状态置为fullfiled,
reject是将Promise的状态置为rejected。
实例:
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成');
resolve('随便什么数据');
}, 2000);
});
异步操作setTimeout在2秒后,输出“执行完成”,并且调用resolve方法。
此处只是new了一个对象,并没有调用它,然而传进去的函数就已经执行了,这是需要注意的一个细节。
所以用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数
例如:
function runAsync(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成');
resolve('异步函数执行过后获取的参数');
}, 2000);
});
return p;
}
runAsync()
执行这个函数我们得到了一个Promise对象,
而Promise对象上有then、catch方法等方法,就可以通过Promise对象执行如下操作:
runAsync().then(function(data){
console.log(data);
//后面可以用传过来的数据做些其他操作
//......
});
在runAsync()的返回上直接调用then方法,then接收一个带参函数,参数为在runAsync中调用resolve时传入的参数。
用回调函数的写法:
function runAsync(callback){
setTimeout(function(){
console.log('执行完成');
callback('随便什么数据');
}, 2000);
}
runAsync(function(data){
console.log(data);
});
Promise与回调函数从基本使用上来说其实是差不多的,
但在多层级回调的时候,Promise的优势就体现出来了,
Promise可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。
Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,
它比传递callback函数要简单、灵活的多。
Promise的正确使用场景:
asyncProOne () {
// 在promise中做异步回调
let pro = new Promise(function (resolve, reject) {
// 异步函数
setTimeout(function () {
console.log('asyProOne Done !')
resolve('belong one')
}, 1000)
})
return pro
},
asyncProTwo () {
let pro2 = new Promise(function (resolve, reject) {
setTimeout(function () {
console.log('asyProTwo Done !')
resolve('belong two')
}, 1000)
})
return pro2
},
asyncProThree () {
let pro3 = new Promise(function (resolve, reject) {
setTimeout(function () {
console.log('asyProThree Done !')
resolve('belong three')
}, 1000)
})
return pro3
}
链式调用:
asyChain () {
let localThis = this
localThis.asyncProOne().then((res) => {
console.log('res', res)
// 返回asy2的对象,可以用作调用asy2的.then方法
return localThis.asyncProTwo()
}).then((res2) => {
console.log('res2', res2)
// 返回asy2的对象,可以用作调用asy3的.then方法
return localThis.asyncProThree()
}).then((res3) => {
console.log('res3', res3)
})
}
层级式调用:
asyChain () {
let localThis = this
localThis.asyncProOne().then((res) => {
console.log('res', res)
console.log('-----------------')
localThis.asyncProTwo().then((res2) => {
console.log('res', res)
console.log('res2', res2)
console.log('-----------------')
localThis.asyncProThree().then((res3) => {
console.log('res', res)
console.log('res2', res2)
console.log('res3', res3)
console.log('-----------------')
})
})
})
}
以至于没过两秒钟会有一个console,并开启下一个Promise异步回调,
在链式的解构上,仍旧可以通过串联.then()函数接受下一次的回调与回调参数
在then方法中,也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了
例如:
asyChain () {
let localThis = this
localThis.asyncProOne().then((res) => {
console.log('res', res)
// 返回asy2的对象,可以用作调用asy2的.then方法
return localThis.asyncProTwo()
}).then((res2) => {
console.log('res2', res2)
// 返回asy2的对象,可以用作调用asy3的.then方法
return '直接返回的数据'
}).then((res3) => {
console.log('res3', res3)
})
}
没有返回Promise对象,链式回调也会中断。作为直接返回数据结果的方式,会将原返回函数中resolve返回无效话
reject的作用就是把Promise的状态置为rejected,这样就能在then中就能捕捉到,然后执行“失败”情况的回调。
上面的示例都是展示Resolve的成功回调,其实还有一个函数reject的失败回调,也是存在的。
实例:
asyProOne () {
let pro = new Promise(function (resolve, reject) {
// 1~10的随机数
let num = Math.ceil(Math.random()*10)
if (num > 5) {
resolve(num)
} else {
reject('不符合要求')
}
})
}
调用:
this.asyProOne().then((resolveData) => {
// 成功回调
console.log(resolveData)
}, (rejectData) => {
// 失败回调
console.log(rejectData)
})
.catch()的效果等同与rejectData的回调。
使用方法是直接在.then()后面加上.catch()即可。
.catch不仅能捕捉rejectData的失败回调,
同时在整个函数体发生错误异常时,也能进行捕捉。
Promise还有一个all方法,提供了并行执行异步操作的能力,
并且在所有异步操作执行完后才执行回调。
实例:
Promise.all([asyProOne, asyProTwo, asyProThree]).then((res) => {
console.log('当所有异步操作执行完毕后,才进行结果回调')
})
Promise.all().then(result)中result是一个数组
all会把所有异步操作的结果放进一个数组中传给then
all中包含的所有异步函数都是并行执行的。
all方法的效果实际上是「数组中所有的异步回调函数,谁执行慢以谁为执行标准」
相对的,race方法就是以「数组中所有异步回调函数,谁执行快以谁为执行标注」
实例:
将asyProOne的定时时间改为500毫秒
Promise.race([asyProOne, asyProTwo, asyProThree]).then(res => {
console.log('这里返回的res实际上就是asyProOne中resolve返回的回调结果')
})
race包裹的三个异步操作同样是并行执行的,当第一个执行完毕的结果作为执行标准返回时,
剩余的异步函数并没有停止,仍旧在执行,知道输出他们的结束标志后整个函数才真正的关闭
这个函数可以适用请求超时的场景:
//请求某个图片资源
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = 'xxxxxx';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise.race([requestImg(), timeout()]).then(function(results){
console.log(results);
}).catch(function(reason){
console.log(reason);
});