再我们日常写项目和面试时,我们都不可避免会遇到Promise。那既然是不可避免的,那就让我们来搞懂他。让我们拿捏了它。
1.Promise是什么?
老规矩先从定义下手。
官方定义:Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值。一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知值的代理。它让你能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。
总结::Promise 是 JS 中进行异步编程的新解决方案,在语法上来说: Promise 是一个构造函数,从功能上来说: promise 对象是用来封装一个异步操作并可以获取其成功/失败的结果值
Promise的作用是什么?
Promise(支持链式调用)解决了回调地狱的代码现象,让代码更便于阅读,更利于维护。
那什么是回调地狱呢?
以前为了解决异步操作的问题,是采用回调函数的方式去处理异步代码的。但是如果我们有多个异步操作,并且嵌套在一起。就会产生回调地狱式的代码,这样的代码会阅读起来就会十分混乱,并且在项目的后期难以维护。而且代码复用性差,耦合度过高,
// 回调地狱
doSomething(function (result) {
doSomethingElse(result, function (newResult) {
doThirdThing(newResult, function (finalResult) {
console.log('Got the final result: ' + finalResult)
}, failureCallback)
}, failureCallback)
}, failureCallback)
//使用 promise 的链式调用解决回调地狱
doSomething().then(function(result) {
return doSomethingElse(result)
})
.then(function(newResult) {
return doThirdThing(newResult)
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult)
})
.catch(failureCallback)
Promise指定回调函数的方式更加灵活
旧:必须在启动异步任务前指定
新(Promise): 启动异步任务 => 返回promie对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
既然我们知道了Promise是用来处理异步操作的,那么异步操作就不可避免的会遇到成功和失败两种情况。那么Promise是如何来处理这两种情况的呢?
Promise的状态
Promise 有三种状态,一个新的Promise必然处于以下几种状态之一:
- 待定(pending):初始状态,既没有被成功,也没有失败。
- 已成功(fulfilled):意味着操作成功完成。
- 已失败(rejected):意味着操作失败。
最初新建的Promise一般都是pending状态,代码根据异步操作的结果来判断是转换成fulfilled状态还是rejected状态。而且只有这 2 种, 且一个 promise 对象只能改变一次无论变为成功还是失败, 都会有一个结果数据,成功的结果数据一般称为 value, 失败的结果数据一般称为 reason
Promise的基本流程图
Promise的使用
new Promise( (resolve, reject) =>{content} ):括号里面要求我们传入一个回调函数,而这个回调函数又要求有两个参数,这两个参数都是两个回调函数。
content:是我们要执行的异步操作,成功时我们调用resolve()函数,失败时调用reject()函数。
==.then((value)=>{ content }):==如果异步操作成功并且调用resolve()函数,promise的转态会变为fulfilled,并且执行them里面的函数,并且会把异步操作的返回值传给them里面。them里面要求传入两个回调函数,而且回调函数要求要有一个参数,形参value。第一个回调函数使成功时执行的,第二个则是失败时执行的
value:可以拿到我们异步操作的返回值
.catch():当异步操作出错时调用reject()函数,会执行catch()。可以在catch里面对错误进行处理
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo');
}, 300);
});
myPromise.then((value) => console.log(value))
API
Promise.resolve()
Promise.resolve(value)方法返回成功的数据或 promise 对象
代码示例:
const promise1 = Promise.resolve(123);
promise1.then((value) => {
console.log(value);
// 输出: 123
});
Promise.reject()
Promise.reject() 方法返回一个带有拒绝原因的 Promise 对象。
代码示例:
function resolved(result) {
console.log('Resolved');
}
function rejected(result) {
console.error(result);
}
Promise.reject(new Error('fail')).then(resolved, rejected);
// 输出: Error: fail
Promise.all()
Promise.all() 方法接收一个 promise 的 数组类型,并且只返回一个Promise实例,那个输入的所有 promise 的 resolve 回调的结果是一个数组。这个Promise的 resolve 回调执行是在所有输入的 promise 的 resolve 回调都结束,或者输入的是一个非Promise的值的时候。它的 reject 回调执行是,只要任何一个输入的 promise 的 reject 回调执行或者输入不合法的 promise 就会立即抛出错误,并且 reject 的是第一个抛出的错误信息。
代码示例:
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// 输出: Array [3, 42, "foo"]
返回值说明:
返回一个新的 promise, 只有所有的 promise 都成功才成功, 只要有一个失败了就
直接失败
Promise.any()
Promise.any() 接收一个由 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);
// 输出:"很快完成"
})
说明:
- Promise.any()不像 Promise.all() 会返回一组成功返回值那样,我们只能得到一个成功返回值(假设至少有一个 promise 成功)。当我们只需要一个 promise 成功返回值,而不关心是哪一个promise成功的值时此方法很有用的。
- 同时,Promise.any()也不像 Promise.race() 总是返回第一个成功的返回值(成功或拒绝)那样,这个方法返回的是第一个兑现的值。这个方法将会忽略掉所有的被拒绝的 promise,直到第一个 promise 兑现
Promise.prototype.catch()
catch() 方法返回一个Promise ,并且处理拒绝的情况
代码示例:
var p1 = new Promise(function(resolve, reject) {
resolve('Success');
});
p1.then(function(value) {
throw 'oh, no!';
}).catch(function(e) {
console.log(e); // "oh, no!"
})
Promise.prototype.finally()
finally() 方法返回一个 Promise。在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。
代码示例:
const aa = new Promise((resolve, reject) => resolve("成功"))
aa.then((value) => console.log(value)).
finally(() => console.log("结束"))
Promise.race()
Promise.race(iterable) 方法返回一个 promise,一旦异步操作中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。
代码示例:
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
});
// 输出: "two"
Promise.prototype.then()
then() 方法返回一个 Promise 。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。
代码示例:
const promise1 = new Promise((resolve, reject) => {
resolve('Success!');
});
promise1.then((value) => {
console.log(value);
// 输出: "Success!"
});
关于Promise的常见问题
如何改变 promise 的状态?
- 调用resolve(value): 如果当前是 pending 就会变为 resolved
- 调用reject(reason): 如果当前是 pending 就会变为 rejected
- 抛出异常(throw): 如果当前是 pending 就会变为 rejected
一个 promise 指定多个成功/失败回调函数, 都会调用吗?
当 promise 改变为对应状态时都会调用
改变 promise 状态和指定回调函数谁先谁后?
- 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
- 如何先改状态再指定回调?
1. 在执行器中直接调用 resolve()/reject()
2. 延迟更长时间才调用 then() - 什么时候才能得到数据?
- 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用,得到数据
- 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
promise.then()返回的新 promise 的结果状态由什么决定?
- 简单表达: 由 then()指定的回调函数执行的结果决定
- 详细表达:
- 如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常
- 如果返回的是非 promise 的任意值, 新 promise 变为 resolved, value 为返回的值
- 如果返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果
promise 如何串连多个操作任务?
- promise 的 then()返回一个新的 promise, 可以开成 then()的链式调用
- 通过 then 的链式调用串连多个同步/异步任务
promise 异常传透?
- 当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调,
- 前面任何操作出了异常, 都会传到最后失败的回调中处理
中断 promise 链?
- 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
- 办法: 在回调函数中返回一个 pendding 状态的 promise 对象