一.什么是Promise?
1.1 理解
-
Promise 是一门新的技术(ES6 规范),是 JS 中进行异步编程的新解决方案(旧方案是单纯使用回调函数,容易形成回调地狱)
-
常见异步操作:①fs 文件操作 ②数据库操作 ③Ajax ④定时器
-
具体表达:
- 从语法上看:Promise是一个构造函数 (自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法)
- 从功能上看:promise对象用来封装一个异步操作并可以获取其成功/失败的结果值
1.2 Promise的状态改变
实例对象promise中的一个属性 PromiseState,表示promise的状态,它有三种状态,pending,fullfilled和rejected
- pending 变为 fullfilled 成功
- pending 变为 rejected 失败
注意点:
- 对象的状态不受外界影响
- 且一个 promise 对象只能改变一次
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果
- 无论成功还是失败,都会有一个结果数据。成功的结果数据一般称为 value,而失败的一般称为 reason
1.3 Promise 的基本流程
1.4 Promise 的基本使用
const promise = new Promise(function(resolve, reject) {
// ... code
if (/* 异步操作成功 */){
resolve(value);
} else { // /* 异步操作失败 */
reject(reason);
}
});
1、Promise构造函数接受一个函数(执行器函数)作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供。
2、resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”,在异步操作成功时调用,并将异步操作的结果,作为参数value传递出去;
3、reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”,在异步操作失败时调用,并将异步操作报出的错误,作为参数error/reason传递出去。
4、Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
promise.then(function(value) {
// success
}, function(reason) {
// failure
});
then方法可以接受两个回调函数作为参数。
第一个回调函数onResolved()是Promise对象的状态变为resolved时调用
第二个回调函数onRejected()是Promise对象的状态变为rejected时调用
这两个函数都是可选的,不一定要提供。它们都接受Promise对象传出的值作为参数
二、为什么要用 Promise
2.1 指定回调函数的方式更加灵活
- 旧语法:必须先指定回调函数,再执行异步任务
// 1. 纯回调的形式
// 成功的回调函数
function successCallback(result) {
console.log("创建成功:" + result);
}
// 失败的回调函数
function failureCallback(error) {
console.log("创建失败:" + error);
}
// 必须先指定回调函数,再执行异步任务
createFileAsync(handleFile, successCallback, failureCallback) // 回调函数在执行异步任务(函数)前就要指定
- Promise: 可以在执行异步任务后再指定回调函数,明显要更加灵活;
启动异步任务 => 返回Promise对象 => 给Promise对象绑定回调函数(甚至可以任务结束后指定多个回调)
// 2. 使用Promise
const promise = createFileAsync(handleFile); // 执行2秒
setTimeout(() => {
promise.then(successCallback, failureCallback) // 也可以获取
}, 3000);
2.2 支持链式调用, 可以解决回调地狱问题
什么是回调地狱?
- 回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调执行的条件
doFirstThing(function(firstResult) {
doSecondThing(firstResult, function(secondResult) {
doThirdThing(secondResult, function(thridResult) {
console.log('Got the third result:' + thridResult)
}, failureCallback)
}, failureCallback)
}, failureCallback)
回调地狱的缺点?
- 不便于阅读
- 不便于异常处理
解终极解决方案?
- Promise链式调用
doFirstThing()
.then(firstResult => doSecondThing(firstResult))
.then(secondResult => doThirdThing(secondResult))
.then(thridResult => {console.log('Got the third result:' + thridResult)})
.catch(failureCallback)
三、如何使用 Promise
Promise 构造函数:
Promise(executor) {}
- executor 函数:同步执行 (resolve, reject) => {}
- resolve 函数:内部定义成功时调用的函数 resove(value)
- reject 函数:内部定义失败时调用的函数 reject(reason)
executor 是执行器,会在 Promise 内部立即同步回调,异步操作 resolve/reject 就在 executor 中执行
Promise原型链上的方法(只有promise实例可以用)
3.1 Promise.prototype.then 方法:p.then(onResolved, onRejected)
指定两个回调(成功+失败)
- onResolved 函数:成功的回调函数 (value) => {}
- onRejected 函数:失败的回调函数 (reason) => {}
指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调,返回一个新的 promise 对象
3.2 Promise.prototype.catch 方法:p.catch(onRejected)
指定失败的回调
- onRejected 函数:失败的回调函数 (reason) => {}
说明:这是then() 的语法糖,相当于 then(undefined, onRejected)
Promise自身的方法(只有Promise可以用,promise实例不能用)
3.3 Promise.resolve 方法:Promise.resolve(value)
value:将被 Promise 对象解析的参数,也可以是一个成功或失败的 Promise 对象
返回一个带着给定值解析过的 Promise 对象,如果参数本身就是一个 Promise 对象,则直接返回这个 Promise 对象
- 如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象
let p1 = Promise.resolve('kobe');
console.log(p1); // Promise {<fulfilled>: kobe}
- 如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
// resolve('OK'); // 成功的Promise
reject('Error');
}));
console.log(p2);
p2.catch(reason => {
console.log(reason);
})
3.4 Promise.reject 方法:Promise.reject(reason)
reason:失败的原因
说明:返回一个失败的 promise 对象
let p = Promise.reject('kobe);
let p2 = Promise.reject('bryant');
let p3 = Promise.reject(new Promise((resolve, reject) => {
resolve('OK');
}));
console.log(p);
console.log(p2);
console.log(p3); //reject 就算返回的promise调用的是resolve
- Promise.resolve()/Promise.reject() 方法就是一个语法糖,用来快速得到Promise对象
3.5 Promise.all 方法:Promise.all(iterable)
iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String
说明:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败
let p1 = new Promise((resolve, reject) => {
resolve('kobe');
})
let p2 = Promise.resolve('bryant');
let p3 = Promise.resolve('james');
const result = Promise.all([p1, p2, p3]);
console.log(result); //全部成功才算成功
let p1 = new Promise((resolve, reject) => {
resolve('kobe');
})
let p2 = Promise.reject('bryant');
let p3 = Promise.reject('james');
const result = Promise.all([p1, p2, p3]);
console.log(result); //只要有一个失败了停止了,并返回第一个报错的信息
3.6 Promise.race方法:Promise.race(iterable)
iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String
说明:返回一个新的 promise,第一个完成的 promise 的结果状态就是最终的结果状态
谁先完成就输出谁(不管是成功还是失败)
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');
//调用
const result = Promise.race([p1, p2, p3]);
console.log(result);
3.7 Promise.allSettled方法:Promise.allSettled(iterable)
iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String
说明:返回一个新的 promise,并且返回的状态始终是成功的,返回的结果包含每一个promise对象的状态和返回值
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
})
let p2 = Promise.reject('Success');
let p3 = Promise.resolve('Oh Yeah');
//调用
const result = Promise.allSettled([p1, p2, p3]);
console.log(result);
四、Promise的几个关键问题
4.1 如何改变 promise 的状态?
- resolve(value):如果当前是 pending 就会变为 resolved
- reject(reason):如果当前是 pending 就会变为 rejected
- 抛出异常:如果当前是 pending 就会变为 rejected
4.2 一个 promise 指定多个成功/失败回调函数,都会调用吗?
当 promise 改变为对应状态时都会调用
const p = new Promise((resolve, reject) => {
//resolve(1)
reject(2)
})
p.then(
value => {},
reason => {console.log('reason',reason)}
)
p.then(
value => {},
reason => {console.log('reason2',reason)}
)
// reason 2
// reason2 2
4.3 promise.then() 返回的新 promise 的结果状态由什么决定?
(1)简单表达:由 then() 指定的回调函数执行的结果决定
(2)详细表达:
① 如果抛出异常,新 promise 变为 rejected,reason 为抛出的异常
② 如果返回的是非 promise 的任意值,新 promise 变为 resolved,value 为返回的值
③ 如果返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果
4.4 promise 如何串联多个操作任务?
(1)promise 的 then() 返回一个新的 promise,可以并成 then() 的链式调用
(2)通过 then 的链式调用串联多个同步/异步任务
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行任务1(异步)')
resolve(1)
}, 1000)
}).then(
value => {
console.log('任务1的结果', value)
console.log('执行任务2(同步)')
return 2 // 同步任务直接return返回结果
}
).then(
value => {
console.log('任务2的结果', value)
return new Promise((resolve, reject) => { // 异步任务需要包裹在Promise对象中
setTimeout(() => {
console.log('执行任务3(异步)')
resolve(3)
}, 1000)
})
}
).then(
value => {
console.log('任务3的结果', value)
}
)
// 执行任务1(异步)
// 任务1的结果 1
// 执行任务2(同步)
// 任务2的结果 2
// 执行任务3(异步)
// 任务3的结果 3
4.5 Promise 异常穿透(传透)?
(1)当使用 promise 的 then 链式调用时,可以在最后指定失败的回调
(2)前面任何操作出了异常,都会传到最后失败的回调中处理
new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
}
).then(
value => {
console.log('onResolved2()', value)
return 3
}
).then(
value => {
console.log('onResolved3()', value)
}
).catch(
reason => {
console.log('onRejected1()', reason)
}
)
// onRejected1() 1
失败的结果是一层一层处理下来的,最后传递到 catch 中
4.6 中断 promise 链?
当使用 promise 的 then 链式调用时,在中间中断,不再调用后面的回调函数
办法:在回调函数中返回一个 pending 状态的 promise 对象(插入一个空promise)
new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
}
).then(
value => {
console.log('onResolved2()', value)
return 3
}
).then(
value => {
console.log('onResolved3()', value)
}
).catch(
reason => {
console.log('onRejected1()', reason)
return new Promise(() => {}) // 返回一个pending的promise
}
).then(
value => {
console.log('onResolved4()', value)
},
reason => {
console.log('onRejected2()', reason)
}
)
// onRejected1() 1
分析:
在 catch 中返回一个新的 promise,且这个 promise 没有结果。
由于,返回的新的 promise 结果决定了后面 then 中的结果,所以后面的 then 中也没有结果。
这就实现了中断 promise链的效果。