promise的使用总结
promise的详细语法请参考MDN 我们知道,一个promise有3种状态:pending(初始状态,既不是成功,也不是失败状态)、fulfilled(意味着操作成功完成)、rejected(味着操作失败)。
new Promise( function(resolve, reject) {...} /* executor */ );
fullfilled完成状态
调用resolve函数会将promise的状态改为fullfilled完成状态:
let promise = new Promise((resolve, reject) => {
setTimeout(()=> {
resolve('我被调用了')
},200)
})
promise.then((result) => {
console.log(result)
}).catch(error => {
console.log(error)
}).finally(() => {
console.log('我最终才会被执行');
})
//我被调用了
//我最终才会被执行
我们还可以使用Promise的静态方法Promise.resolve(value)
来返回一个Promise:
let promise = Promise.resolve('我是resolve');
promise.then(result => {
console.log(result)
})
//我是resolve
//也可以是链式的写法
Promise.resolve("Success").then(function(value) {
console.log(value); // "Success"
});
resolve另一个promise
let original = Promise.resolve(313);
let cast = Promise.resolve(original);
cast.then(value => {
console.log('value: ' + value);
});
console.log('original === cast ? ' + (original === cast));
//打印顺序如下,这里有一个同步异步先后执行的区别
//original === cast ? true
//value: 313
日志顺序颠倒其实是由于异步地调用then 方法.至于then()方法,在后面我们会说到。
rejected失败状态
调用reject函数会将promise的状态改为rejected失败状态:
let promise = new Promise((resolve, reject) => {
setTimeout(()=> {
reject('我被调用了')
},200)
})
promise.then((result) => {
console.log(result)
}).catch(error => {
console.log('我发生了错误')
console.log(error)
}).finally(() => {
console.log('我最终才会被执行');
})
//我发生了错误
// 我被调用了
// 我最终才会被执行
同样的,我们还可以使用Promise的静态方法Promise.reject(reason)
来返回一个被拒绝的Promise对象:
Promise.reject('糟糕!发生了错误').then(res => {
console.log('我不会被调用');
}, reason => {
console.log('我被调用了:', reason);
})
//我被调用了: 糟糕!发生了错误
then()方法的使用
then() 方法返回一个 Promise。它最多需要有两个参数:Promise 的成功情况resolve和失败情况reject的回调函数,它的语法如下:
p.then(onFulfilled[, onRejected]);
p.then(value => {
// fulfillment
}, reason => {
// rejection
});
成功状态:
let promise = new Promise((resolve, reject) => {
setTimeout(()=> {
resolve('我是成功的回调')
},200)
})
promise.then((res) => {
console.log(res) //我是成功的回调
}, (error) => {
console.log(error)
})
失败状态:
let promise = new Promise((resolve, reject) => {
setTimeout(()=> {
reject('我是失败的回调')
},200)
})
promise.then((res) => {
console.log(res)
}, (error) => {
console.log(error) //我是失败的回调
})
then的回调时机:
const resolvedProm = Promise.resolve(313);
let thenProm = resolvedProm.then(value => {
console.log("当前线程循环结束前调用: " + value);
return value;
});
console.log('调用了:', thenProm);
setTimeout(() => {
console.log('setTimeout的回调:',thenProm);
});
/**
* 调用了: Promise {<pending>}
* 当前线程循环结束前调用: 313
* setTimeout的回调: Promise {<resolved>: 313}
*/
then方法的链式调用
then 方法返回一个 Promise 对象,那么这就可以使用链式调用了:
Promise.resolve('first').then(result => {
return new Promise((resolve, reject) => {
setTimeout(() => {
result += '---->second'
resolve(result)
}, 2000)
})
}).then(result => {
return new Promise((resolve, reject) => {
setTimeout(() => {
result += '---->third'
resolve(result)
}, 2000)
})
}).then(result => {
console.log('最终的结果是:',result); //最终的结果是: first---->second---->third
})
当一个值只是从一个 then 内部返回时,它将等价地返回 Promise.resolve(<由被调用的处理程序返回的值>):
Promise.resolve('first').then(result => {
return result + '---->second';
}).then(result => {
return result + '---->third';
}).then(result => {
console.log('最终的结果是:', result);
});
console.log('调用了');
/**
* 调用了
* 最终的结果是: first---->second---->third
*/
注意下面的打印顺序,’哈哈哈,我也调用了: 2’是最后被调用的:
const p2 = new Promise((resolve, reject) => {
resolve(1)
});
p2.then(function(value) {
console.log('调用了:', value);
return value + 1;
}).then(function(value) {
console.log('哈哈哈,我也调用了:', value);
});
p2.then(function(value) {
console.log('哦哦哦,看来我也调用了:', value);
});
p2.then(function(value) {
console.log('啊啊啊,我也调用了:', value);
});
/**
* 调用了: 1
* 哦哦哦,看来我也调用了: 1
* 啊啊啊,我也调用了: 1
* 哈哈哈,我也调用了: 2
*/
在catch回调之后,还能继续then的回调:
Promise.resolve()
.then(() => {
// 使 .then() 返回一个 rejected promise
throw new Error('发生了一个错误!');
})
.catch(error => {
console.error('onRejected被回调了: ' + error.message);
})
.then(() => {
console.log("即便reject被回调了,我还是能被调用...");
});
/**
* onRejected被回调了: 发生了一个错误!
* 即便reject被回调了,我还是能被调用...
*/
Promise.all(iterable)方法
Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数可以是promise对象,也可以是普通的值,我们可以封装iterable为一个数组。如果iterable中包含promise对象,并且 promise有一个失败(rejected),此实例的回调就会失败(reject),失败原因的是第一个失败 promise 的结果。
全部都是promise对象:
var promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise1, 我被调用了')
},2000)
})
var promise2 = Promise.resolve('我是promise2, 我被调用了')
var promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise3, 我被调用了')
},1200)
})
var promise4 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise4, 我被调用了')
},4000)
})
Promise.all([promise1, promise2, promise3, promise4]).then(result => {
console.log(result)
})
// ["我是promise1, 我被调用了", "我是promise2, 我被调用了", "我是promise3, 我被调用了", "我是promise4, 我被调用了"]
数组中可以传入非promise数据:
tips:如果参数中包含非 promise 值,这些值将被忽略,但仍然会被放在返回数组中(如果 promise 完成的话)
let promise1 = Promise.resolve('我是promise1, 我被调用了')
let name = 'LXX'
let age = 18
Promise.all([promise1, name, age]).then(result => {
console.log(result)
})
// ["我是promise1, 我被调用了", "LXX", 18]
注意:返回值将会按照参数内的 promise 顺序排列,而不是由调用 promise 的完成顺序决定。
如果传入的 promise 中有失败(rejected):
let promise1 = Promise.resolve('我是promise1, 我被调用了')
let name = 'LXX'
let age = 18
let promise2 = Promise.resolve('我是promise2, 我被调用了')
let promise3 = Promise.reject('我是promise3, 我被拒绝了')
let promise4 = Promise.resolve('我是promise4, 我被调用了')
Promise.all([promise1, name, age, promise2, promise3, promise4]).then(result => {
console.log(result)
})
// Uncaught (in promise) 我是promise3, 我被拒绝了
如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成
异步性:
let promise1 = Promise.resolve('我是promise1, 我被调用了')
let promise2 = Promise.resolve('我是promise2, 我被调用了')
let p = Promise.all([promise1, promise2])
console.log('我的状态是:');
console.log(p);
p.then((result) => {
console.log('我在then的回调:');
console.log(result)
console.log(p);
})
setTimeout(() => {
console.log('我在setTimeout的回调:');
console.log(p);
})
console.log('我被执行了');
//我的状态是:
//Promise {<pending>}
//我被执行了
//我在then的回调:
//(2) ["我是promise1, 我被调用了", "我是promise2, 我被调用了"]
//Promise {<resolved>: Array(2)}
//我在setTimeout的回调:
//Promise {<resolved>: Array(2)}
let promise1 = Promise.resolve('我是promise1, 我被调用了')
let promise2 = Promise.resolve('我是promise2, 我被调用了')
let p = Promise.all([promise1, promise2])
console.log('我的状态是:');
console.log(p);
setTimeout(() => {
console.log('我在setTimeout的回调:');
console.log(p);
})
p.then((result) => {
console.log('我在then的回调:');
console.log(result)
console.log(p);
})
console.log('我被执行了');
//我的状态是:
//Promise {<pending>}
//我被执行了
//我在then的回调:
//(2) ["我是promise1, 我被调用了", "我是promise2, 我被调用了"]
//Promise {<resolved>: Array(2)}
//我在setTimeout的回调:
//Promise {<resolved>: Array(2)}
由于我们传入的是resolve的promise数组,所以会触发promise.all。我们发现,无论setTimeout是在p.then前还是后面,都是最后调用。
Promise.race(iterable)的用法
race 函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, "one");
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "two");
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// 两个都完成,但 p2 更快
});
var p3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "three");
});
var p4 = new Promise(function(resolve, reject) {
setTimeout(reject, 500, "four");
});
Promise.race([p3, p4]).then(function(value) {
console.log(value); // "three"
// p3 更快,所以它完成了
}, function(reason) {
// 未被调用
});
var p5 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, "five");
});
var p6 = new Promise(function(resolve, reject) {
setTimeout(reject, 100, "six");
});
Promise.race([p5, p6]).then(function(value) {
// 未被调用
}, function(reason) {
console.log(reason); // "six"
// p6 更快,所以它失败了
});
举例
使用Promise.all
的场景举例
在uni-app中,有一个复杂的页面,需要多个接口返回数据,我们希望在多个接口数据未全部返回数据时使用loading,全部成功后结束loading。多个接口中如果有一个返回异常报错,则提示用户数据异常。这种场景下,我们就可以使用promise了。
如上图,我们需要同时请求多个接口,接口之间并没有数据依赖,那么就可以使用Promise.all了。
下面是简化后的代码:
onLoad() { //uni-app生命周期函数
this.requestData()
},
methods: {
// 刷新页面数据
requestData() {
tip.showLoading()
//将多个请求接口添加到数组中
const promises = [this.getRealSales(), this.getMessage(), this.getVideo(), this.getTemplate(), this.getTask(), this
.getUomMap()
]
Promise.all(promises)
.then(res => {})
.catch(err => {})
.finally(() => {
tip.hideLoading()
})
},
//共需要请求6个接口,此处只取出一个接口做说明
getRealSales() {// 获取实时销售数据
return this.$api
.getRealSales()
.then(res => {
this.real_sales = tool.filterObjProp(res, ['sales_amount', 'passenger_flow', 'each_profit'])
})
.catch(err => tip.showToast(err))
},
}
你可以选择在拿到全部接口数据后统一做处理,也可以分别在每个接口返回后做处理,然后Promise.all中只是有一个空实现。
使用await
操作符
await 操作符用于等待一个Promise 对象。它只能在异步函数 async function 中使用。
function test1 () {
return new Promise(resolve => {
setTimeout(() => {
console.log('4s后,test1请求到了数据');
resolve(123)
}, 4000)
})
}
function test2 (data) {
return new Promise(resolve => {
setTimeout(() => {
console.log('2s后,test2请求到了数据');
resolve(data * 1000)
}, 2000)
})
}
async function requestData () {
try {
console.log('我开始执行了');
let response = await this.test1()
response = await this.test2(response)
console.log(response);
} catch (e) {
throw e
} finally {
console.log('我最后才会被执行');
}
}
requestData()
/**
* 我开始执行了
* 4s后,test1请求到了数据
* 2s后,test2请求到了数据
* 123000
* 我最后才会被执行
*/
如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。所以,如果接口直接有依赖关系,那么就可以使用await这种方式了。