Promise
Promise 是什么?
Promise 简单来说,是一种异步编程解决方案,主要用来解决大资源导致页面阻塞问题,还可以来解决地狱回调,让代码更加优雅。
概况
Promise 对象有个特性,一旦创建就会立即执行,即使它本身是个异步函数。
const prom = new Promise((resolve)=>{
console.log('创建了一个Promise');
resolve('Promise 回调')
})
prom.then(res=>{
console.log(res);
})
console.log("某个同步任务");
// 输出结果
// 创建了一个Promise
// 某个同步任务
// Promise 回调
我们可以看到第一个打印的是 创建了一个Promise,所以要注意 Promise 一旦创建就会立即执行的特性。
内置状态
一个 Promise 必然处于以下几种状态之一:
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled):意味着操作成功完成。
- 已拒绝(rejected):意味着操作失败。
一个promise执行结束,要么是 已兑现 ,要么是 已拒绝
解决的痛点
解决回调地狱!!!!
有这么个情况,某个接口需要依赖另外一个接口的值,那么以前的写法是:
请求1 (function (请求结果1) {
请求2 (function (请求结果2) {
请求3 (function (请求结果3) {
})
})
})
有了Promise,可以这么写:
const p = new Promise((resolve)=>{
setTimeout(function() {
resolve("接口请求成功-1")
}, 10);
})
p.then(res=>{
console.log("接口1的值", res)
return new Promise((resolve)=>{
setTimeout(function() {
resolve("接口请求成功-2")
}, 10);
})
}).then(res=>{
console.log("接口2的值", res)
console.log("执行接口3")
})
使用
Promise是一个全局的内置对象,通过 new Promise 可以创建一个 Promise 对象。 接收一个函数,回调了resolve(成功回调),reject(失败) 两个函数。
new Promise((resolve, reject)=>{
console.log('创建一个Promise');
});
// Promise 创建就会立即执行
// 创建一个Promise
then、catch以及finally
const promise = new Promise((resolve, reject)=>{
resolve("成功...")
});
const promise2 = new Promise((resolve, reject)=>{
reject("失败...")
});
promise.then(res=>{
console.log(res) // 成功...
}).finally(()=>{
console.log("成功了执行")
})
promise2.catch(res=>{
console.log(res) // 失败...
}).finally(()=>{
console.log("失败了也执行")
})
-
resolve 通过 .then 捕获 resolve 函数的作用是,将 Promise 对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
-
reject 通过 .catch 捕获 reject 函数的作用是,将 Promise 对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
-
finally 不管调用成功与否,都一定会执行,适合处理一些固定执行的逻辑
链式调用
Promise 中 return 的数据,会被自动包成 Promise 对象,可以通过 .then 或 .catch 二次捕获。
- 直接 return 会自动 包装 成一个 promise 实例,状态是 fulfilled,并且可以通过 .then捕获
- return 一个 reject,可以在 catch 中 被捕获.
- return 一个 resolve,可以在 then 中捕获
var flag = true
var p = new Promise((resolve, reject) => {
console.log("创建一个 promise");
setTimeout(()=> {
resolve(flag)
}, 100)
})
p.then(res => {
console.log('then 1')
if(res) {
console.log('reject from then 1')
return Promise.reject(res)
}
return res
}).then(res => {
console.log('then 2')
}).catch(err => {
console.log('catch 1')
return err
}).catch(err => {
console.log('catch 2')
}).then(res => {
console.log('then 3')
return Promise.reject(res)
}).catch(err=>{
console.log('catch 4')
return Promise.resolve(err)
}).then(res => {
console.log('then 5:', res)
}).then((res)=>{
console.log('即使不return也会调用 then')
})
返回结果如下
创建一个 promise
then 1
reject from then 1
catch 1
then 3
catch 4
then 5: true
即使不return也会调用 then
async/await
其实是 Promise 的一种语法糖。像结合了 Generators 和 Promise
async/await 就是为了解决Promise冗余的 .then,让代码更加简洁,语义化。
await
await的意思就是等待。它后面可以跟一个表达式
- 如果是值(如字符串、数字、普通对象等等)的话,返回值就是本身的值。
- 是一个
promise对象。await会等待这个promise的状态由pending转为fulfilled或者rejected。在此期间它会阻塞,延迟执行await语句后面的语句。
如果promise对象的结果是resolve,它会将resolve的值,作为await表达式的运算结果。
- 使用await 必须结合 async使用,需要包裹在 async 函数中
写个例子:
// 模拟请求后端接口
function asyncFn () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
if (true) {
console.log('resolve console')
resolve('resolve return')
} else {
reject('reject return')
}
}, 2000)
})
}
// promise
asyncFn().then((res) => {
console.log("promise等待", res)
}, (err) => {
console.log(err)
})
// await,需要结合 async
async function awaitFn(){
try {
var res = await asyncFn()
console.log("await等待-promise resolve的值:", res)
var num = await 5;
console.log("await等待-普通对象:", num);
// ...甚至更多接口
} catch(err) {
console.log(err)
}
}
awaitFn();
输出结果如下:
resolve console
promise等待 resolve return
resolve console
await等待-promise resolve的值: resolve return
await等待-普通对象:5
async
await 函数会强行阻塞代码运行,使用 async 声明,会将函数包了一层 promise,这样内部的异步操作其实就是由pending转为resolve或者reject的过程。这样函数本身就能够随意调用,函数内部的await也不会再影响到函数外部的代码执行。
简单来说就是, async把函数转为promise对象,可以解决 await强制阻塞的缺点
async function asyncFn () {
return 'async'
}
console.log(asyncFn())
console.log(asyncFn().then((res)=>{
console.log("值",res)
}))
会发现打印的是一个promise对象。而且是Promise.resolve对象。resolve的值就是asyncFn的函数返回值async。
特点总结
- await 会阻塞代码
- 使用 async包裹的函数,会变成一个 promise 对象,接收的是 resolve 的值
- await 必须配合 async 使用
常用方法
resolve
返回一个以给定值解析后的 Promise 对象。如果这个值是一个 promise,那么将返回这个 promise;如果这个值是 thenable(即带有 then 方法),返回的 promise 会“跟随”这个 thenable 的对象,采用它的最终状态;否则返回的 promise 将以此值完成。此函数将类 promise 对象的多层嵌套展平。
一般用来处理接口调用成功
// 返回一个普通数组
var p = Promise.resolve([1,2,3]);
p.then(function(v) {
console.log(v[0]); // 1
});
// 返回一个 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));
/*
* 打印顺序如下,这里有一个同步异步先后执行的区别
* original === cast ? true
* value: 33
*/
reject
调用失败,返回Promise.reject() 方法返回一个带有拒绝原因的 Promise 对象。
一般用来处理接口调用失败
Promise.reject(new Error('fail')).then(function() {
// not called
}, function(error) {
console.error(error); // Stacktrace
});
all
Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set 都属于 ES6 的 iterable 类型)的输入,并且只返回一个Promise实例,回调的结果是一个数组。
示例:
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
setTimeout(resolve, 4000, 'four');
});
var p5 = new Promise((resolve, reject) => {
reject('reject');
});
// 等待所有 promise 完成,执行成功回调
Promise.all([p1, p2, p3, p4]).then(values => {
console.log(values);
}, reason => {
console.log(reason)
});
// 有失败请求,会直接结束,执行失败回调
Promise.all([p1, p2, p3, p4, p5]).then(values => {
console.log(values);
}).catch(reason => {
console.log(reason)
});
// logs
// reject
// [ 'one', 'two', 'three', 'four' ]
传入空的迭代对象,会立即结束执行
var p = Promise.all([]); // will be immediately resolved
var p2 = Promise.all([1337, "hi"]); // non-promise values will be ignored, but the evaluation will be done asynchronously
console.log(p);
console.log(p2)
setTimeout(function(){
console.log('the stack is now empty');
console.log(p2);
});
// logs
// Promise { <state>: "fulfilled", <value>: Array[0] }
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "fulfilled", <value>: Array[2] }
从调用结果我们可以看出:
- 含有 p5 的请求,会走 .catch
- 存在失败回调会立即结束
- 传入空的迭代对象,代码执行是同步性的
特点总结:
Promise.all执行结束的标识是,所有都完成或第一个失败。Promise.all接收的迭代对象为空时是同步的。
allSettled
大体上的使用跟 .all 类似,最大的不同的点是存在失败回调不会结束函数执行
看个例子:
Promise.allSettled([
Promise.resolve(33),
Promise.reject(new Error("an error")),
new Promise((resolve) => setTimeout(() => resolve(66), 0)),
99,
]).then((values) => console.log(values));
// [
// { status: 'fulfilled', value: 33 },
// { status: 'rejected', reason: Error: an error }
// { status: 'fulfilled', value: 66 },
// { status: 'fulfilled', value: 99 },
// ]
从调用结果我们可以看出:
- 存在失败,依旧会执行代码,直到所有 promise 调用完毕才结束
- 返回的数据是键值对数组,成功的值在 value,失败的值在 reason
any
Promise.any() 接收一个由 Promise 所组成的可迭代对象,该方法会返回一个新的 promise,只要有一个 promise 执行成功,该函数就结束。
示例:
执行第一个成功
const pErr = new Promise((resolve, reject) => {
reject("总是失败");
});
const pSlow = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "最终完成");
});
const pFast = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "很快完成");
});
Promise.any([pErr, pSlow, pFast]).then((value) => {
console.log(value);
// pFast fulfils first
})
// logs
// "很快完成"
没有成功回调
const pErr = new Promise((resolve, reject) => {
reject('总是失败');
});
Promise.any([pErr]).catch((err) => {
console.log(err);
})
// "AggregateError: No Promise in Promise.any was resolved"
从调用结果我们可以看出:
- 回调第一个成功的 promise,执行.then,结束调用
- 没有成功的 promise,执行.catch,结束调用
race
Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。
示例:
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) {
// 未被调用
}).catch(err=>{
console.log(err); // "six"
// p6 更快,所以它失败了
});
从调用结果我们可以看出: 只会回调一个结果,不管失败、成功,哪个快,回调哪个。