ES6 - Promise
Promise API & 手写 Promise
概念
本质上 Promise 是一个函数返回的对象,我们可以在它上面绑定回调函数,这样我们就不需要在一开始把回调函数作为参数传入这个函数了,举个例子:
function B(){};
function C(){};
function A(args,B,C){}; // 函数A接受参数和两个回调函数
转换成 Promise 形式:
A(args).then(B,C)
链式调用
概念
连续执行两个或者多个异步操作是一个常见的需求,在上一个操作执行成功之后,开始下一个的操作,并带着上一步操作所返回的结果。我们可以通过创造一个 Promise 链来实现这种需求。
每个 Promise 对象都可以在其上多次调用 then 方法,then 方法会返回一个全新的 Promise 对象,下一个 then 方法其实是依附于返回的 Promise 对象,这样层层挂钩就形成了 promise 链:
new Promise((resolve,reject) => {
resolve('start')
}).then(data => {
console.log(data); // 'start', then 的参数是上一个 Promise 对象的 resolve 的参数
return Promise.resolve(1);
}).then(data => {
console.log(data); // 1
})
中断调用
如果要中途跳出链式调用,就需要用reject()
抛出异常,用catch()
去接受异常,为了和其他报错区别开来,可以定义一个 manualExit 的变量作为 reject 的参数:
new Promise((resolve,reject) => {
resolve('start')
}).then(data => {
return Promise.resolve(1);
}).then(data => {
reject({
manualExit : true
})
}).catch(err => {
if(err.manualExit){
// 手动退出
}else{
// 其他异常
}
}).then(data => {
// 依然会执行
})
当 reject 执行时,会跳过后面的 then 方法,直接执行 catch,这里需要注意的是,catch 方法最好放在最后,因为 catch 方法会返回一个新的 resolved 的 Promise 实例,所以在 catch 之后的 then 方法依旧会被执行。
属性
.finally
ES2018 引入标准,不论 Promise 对象最后是何种状态(fulfilled / rejected),都会执行该操作,如果代码需要在成功或者失败两种情况下都运行,写入.finally()
中可以减少代码量。
promise
.then(result => {
console.log('X')
})
.catch(error => {
console.log('X')
})
// 等同于
promise
.finally(() => {
console.log('X')
});
.all
const p = Promise.all([p1, p2, p3]);
.all()
相当于&&
操作符,它接受由多个 Promise 实例组成的数组,当所有实例的状态都变为 fulfilled 时,p 的状态才会变成 fulfilled,只要其中一个实例被 rejected,那么状态就会变成 rejected。
const p1 = new Promise((resolve, reject) => {
resolve('resolved');
})
.then(result => result)
const p2 = new Promise((resolve, reject) => {
throw new Error('rejected');
})
.then(result => result)
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e)); // 由于p2报错,所以会进入catch
如果 p2 设置了 catch,并且返回了错误,那结果就不一样了。catch 方法会返回一个新的 resolved 的 Promise 实例,p2 实际上是指向了这个实例,导致 .all()
方法中两个实例都是 resolved,因此会进入.then()
而非.catch()
。
...
const p2 = new Promise((resolve, reject) => {
throw new Error('rejected'); // or reject('rejected')
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result)) // 实际进入的是then
.catch(e => console.log(e));
.any
.any()
相当于||
操作符,它接受由多个 Promise 实例组成的数组,只要有一个实例的状态变为 fulfilled 时,p 的状态就会变成 fulfilled,当所有实例的状态都变为 rejected 时 p 的状态才会变成 rejected。
const resolved = Promise.resolve(2);
const rejected = Promise.reject(-1);
const anyPromise = Promise.any([resolved, rejected]);
anyPromise.then(function (results) {
console.log(results); // 2
});
.race
const p = Promise.race([p1, p2, p3]);
.race()
接受由多个 Promise 实例组成的数组,当其中一个实例的状态发生改变时,p 的状态就会改变并将率先改变的实例的返回值(不论成功/失败)传递给回调函数。
race 的特性可以用于监听 fetch 请求是否超时:
function _fetch(fetch_promise,timeout){
return Promise.race([
fetch_promise,
new Promise((resolve, reject)=>{
setTimeout(()=>{
reject(new Error('timeout'));
},timeout)
})
]).then(()=>{
console.log('success')
}).catch(()=>{
console.log('error');
})
}
.allSettled
ES2020 引入标准,.allSettled()
接受由多个 Promise 实例组成的数组,返回一个新的 Promise,只有所有的实例都返回结果时(不论 fulfilled 还是 rejected)才会返回每个 Promise 的状态(由 {status,reason/value}
组成的数组),要注意的时.allSettled()
不会失败。
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
console.log(results);
});
// [{status: 'fulfilled', value: 42}, {status: 'rejected', reason: -1}]
.resolve
.resolve()
可以将现有对象转换成 promise 对象,有以下几种情况:
对象是 Promise 实例:直接返回;
对象是由 then 方法的实例:转换成 Promise 对象,然后立即执行 then 内的方法;
let thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); // 42 });
对象不具备 then 方法或者压根不是对象(甚至没有):返回一个新的 Promise 对象,状态为 resolved:
const p = Promise.resolve('Hello'); p.then(function (s){ console.log(s) }); // Hello
const p = Promise.resolve(); p.then(function () { // ... });
.reject
.reject(reason)
返回一个新的 Promise 实例,该实例的状态为 rejected,传入的 reason 会原封不动的返回。
const p = Promise.reject('rejected');
// 等同于
const p = new Promise((resolve, reject) => reject('rejected'))
p.then(null, function (s) {
console.log(s) // rejected
});
手写 Promise
function myPromise(constructor){
let self=this;
self.status="pending"; // 定义状态改变前的初始状态
self.value=undefined; // 定义状态为resolved的时候的状态
self.reason=undefined; // 定义状态为rejected的时候的状态
self.then=function(onFullfilled, onRejected){
let self=this;
switch(self.status){
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
function resolve(value){
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
async/await
async/await 是用来优化 Promise 的回调问题,被称作是异步的终极解决方案。
async:
Promise 的语法糖,返回一个 Promise,如果在函数中 return 一个常量,async 会把这个常量通过 Promise.resolve() 封装成 Promise 对象,如果返回的是 Promise 对象,则以返回的 Promise 为准。
返回常量:
async function hello() { return "Hello" };
hello(); // Promise {<fulfilled>: "Hello"}
返回 Promise:
async function hello() { return Promise.reject("Hello") };
hello(); // Promise {<rejected>: "Hello"}
await:
await 可以放在任何的异步前面,会阻塞代码运行直到其右侧 Promise 完成,然后返回结果值。
async function hello() {
return greeting = await Promise.resolve("Hello");
};
hello().then(val=>console.log(val)); // Hello
ES6 - Promise