Promise
简介
- 在 JS 中执行异步操作的方案 ——
Promise
Promise
是一个构造函数,用来封装异步操作,可以获取异步操作的返回值
使用 Promise 执行异步操作的优点:
- 比传统的解决方案(回调函数)更灵活、更强大
- 回调函数实现异步:必须在执行异步任务之前,指定回调函数
Promise
实现异步:执行异步任务 → 返回Promise
实例 → 给Promise
实例绑定回调函数
- 支持链式调用,避免 “回调地狱”(回调地狱:疯狂嵌套回调函数。不便于阅读、处理)
代价:代码量翻倍
使用 Promise 执行异步操作的缺点:
- 无法取消
Promise
对象,一旦新建,就会立即执行,且无法中断 - 如果不设置函数参数,
Promise
对象内部抛出的错误,不会反应到外部 - 当处于
pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始 or 即将完成)
构造函数 Promise
Promise(executor)
回调函数 executor:也叫 “执行器”,格式为 (resolve, reject) => {}
- 如果不调用回调函数 resolve / reject,则 Promise 实例的状态将一直是 pending
- 回调函数 resolve:调用 resolve 会改变 Promise 实例的状态 pending → fulfilled
- 回调函数 reject:调用 reject 会改变 Promise 实例的状态 pending → rejected
let pro0 = new Promise(() => {});
console.log("pro0", pro0); // pro0 Promise {<pending>}
let pro1 = new Promise((resolve) => {
resolve();
});
console.log("pro1", pro1); // pro1 Promise {<fulfilled>: undefined}
let pro2 = new Promise((resolve, reject) => {
reject();
});
console.log("pro2", pro2); // pro2 Promise {<rejected>: undefined}
- 执行器 executor 是同步执行的
console.log("before executor"); // before executor
new Promise(() => {
console.log("executor"); // executor
});
console.log("after executor"); // after executor
上例输出顺序:before executor
→ executor
→ after executor
如果执行器是异步执行的,则输出顺序会是:before executor
→ after executor
→ executor
Promise 实例的值
-
调用
resolve(val)
/reject(val)
变更状态时,传入的参数数据val
就是该 Promise 实例的值即 resolve & reject 都可接收 1 个参数,作为 Promise 实例的值
-
Promise 实例的值可通过查看属性
[[PromiseResult]]
得知
let pro1 = new Promise((resolve) => {
resolve("pro1 resolve");
});
console.log("pro1", pro1); // pro1 Promise {<fulfilled>: 'pro1 resolve'}
let pro2 = new Promise((resolve, reject) => {
reject("pro2 reject");
});
console.log("pro2", pro2); // pro2 Promise {<rejected>: 'pro2 reject'}
Promise 实例的状态
-
待定 pending:进行中, 此时异步操作尚未完成
-
完成 fulfilled:表示异步操作成功
-
拒绝 rejected:表示异步操作失败
-
调用 resolve 方法,Promise 实例的状态从 pending → fulfilled
-
调用 reject 方法,Promise 实例的状态从 pending → rejected
-
抛出错误,Promise 实例的状态从 pending → rejected
let pro1 = new Promise((resolve, reject) => {
resolve("value");
});
console.log(pro1); // Promise {<fulfilled>: 'value'}
let pro2 = new Promise((resolve, reject) => {
reject("reason");
});
console.log(pro2); // Promise {<rejected>: 'reason'}
let pro3 = new Promise((resolve, reject) => {
throw "error";
});
console.log(pro3); // Promise {<rejected>: 'error'}
- Promise 实例的状态可通过查看属性
[[PromiseState]]
得知 - 状态只能从 pending → fulfilled / rejected,且状态一旦变更就不会再改变
let pro1 = new Promise((resolve, reject) => {
resolve(); // 写在前面,生效
reject(); // 后面的不会生效
});
console.log("pro1", pro1); // pro1 Promise {<fulfilled>: undefined}
let pro2 = new Promise((resolve, reject) => {
resolve(); // 写在前面,生效
throw "error"; // 后面的不会生效
});
console.log("pro2", pro2); // pro2 Promise {<fulfilled>: undefined}
resolve
& reject
Promise.resolve(value)
- 用于创建 fulfilled 状态的 Promise 实例
① 参数 value
是 Promise 实例 → 直接返回该实例
let p1 = new Promise(resolve => {
resolve('error');
});
console.log(p1); // Promise {<rejected>: 'error'}
let p2 = Promise.resolve(p1);
console.log(p2); // Promise {<rejected>: 'error'}
console.log(p1 === p2); // true
② 参数 value
不是 Promise 实例 → 返回 fulfilled 状态的 Promise 实例,值为参数值
let pro = Promise.resolve(123);
console.log(pro); // Promise {<fulfilled>: 123}
③ 无参数 → 返回 fulfilled 状态的 Promise
实例,值为 undefined
let pro = Promise.resolve();
console.log(pro); // Promise {<fulfilled>: undefined}
Promise.reject(reason)
- 用于创建 rejected 状态的 Promise 实例
- 参数
reason
为该 Promise 实例的值,传入什么,Promise 实例的值就是什么
如果不设置参数,则该 Promise 实例的值为undefined
let pro = new Promise((resolve, reject) => {
resolve("pro resolve");
});
let pro1 = Promise.reject(pro);
console.log("pro1", pro1); // pro1 Promise {<rejected>: Promise}
let pro2 = Promise.reject("reason");
console.log("pro2", pro2); // pro2 Promise {<rejected>: 'reason'}
let pro3 = Promise.reject();
console.log("pro3", pro3); // pro3 Promise {<rejected>: undefined}
then
& catch
& finally
- 构造函数
Promise(executor)
的执行器executor
是同步执行的 - 但 Promise 实例的
then
/catch
/finally
操作是异步执行的,属于异步操作里面的微任务
Promise.prototype.then
Promise.prototype.then(onResolve, onReject)
-
回调函数 onResolve:处理状态为 fulfilled 的 Promise 实例
-
回调函数 onReject:处理状态为 rejected 的 Promise 实例
onReject & onResolve 都可接收 1 个参数,参数值为调用该方法的 Promise 实例的值
- onResolve & onReject 会返回新的 Promise 实例
- 因为是 Promise.prototype 上的方法,所以可以通过 Promise 实例直接调用
let p1 = Promise.resolve("p1 fulfilled");
p1.then(value => {
console.log("value", value); // value p1 fulfilled
});
let p2 = Promise.reject("p2 rejected");
p2.then(null, reason => {
console.log("reason", reason); // reason p2 rejected
});
关于
then
方法返回的 Promise 实例
return
Promise 对象
let pro = Promise.resolve('pro fulfilled');
let proThen = pro.then(value => {
console.log("value", value); // value pro fulfilled
return Promise.reject('superman');
}); // Promise {<rejected>: 'superman'}
return
非 Promise 对象:返回 fulfilled 状态的 Promise 实例,值为return
的数据值
let pro = Promise.resolve('pro fulfilled');
let proThen = pro.then(value => {
console.log("value", value); // value pro fulfilled
return 'superman';
}); // Promise {<fulfilled>: 'superman'}
- 没有
return
语句:返回 fulfilled 状态的 Promise 实例,值为undefined
let pro = new Promise((resolve) => {
resolve('pro fulfilled');
});
let proThen = pro.then(value => {
console.log("value", value); // value pro fulfilled
}); // Promise {<fulfilled>: undefined}
- 抛出错误:返回 rejected 状态的 Promise 实例,值为抛出的错误信息
let pro = Promise.resolve('pro fulfilled');
let proThen = pro.then(value => {
console.log("value", value); // value pro fulfilled
throw 'error';
}); // Promise {<rejected>: 'error'}
Promise.prototype.catch
Promise.prototype.catch(onReject)
-
回调函数 onReject:处理状态为 rejected 的 Promise 实例
参数函数可接收 1 个参数,参数值为调用该方法的 Promise 实例的值
- onReject 会返回新的 Promise 实例
catch
是then
的语法糖,相当于then(null, onRejected)
let pro = new Promise((resolve, reject) => {
reject('error');
});
pro.catch(reason => {
console.log("reason", reason); // reason error
});
Promise.prototype.finally
- 不论状态为 fulfilled 还是 rejected,Promise 实例都会执行 finally 语句
- 但是 pending 状态的 Promise 实例不会执行 finally 语句
- finally 方法不接收参数
let pro1 = Promise.resolve();
pro1.finally(_ => {
console.log("pro1 resolve"); // pro1 resolve
});
let pro2 = Promise.reject();
pro2.finally(_ => {
console.log("pro2 reject"); // pro2 reject
});
let pro3 = new Promise(() => {});
pro3.finally(_ => {
console.log("pro3 pending");
});
- finally 方法会返回新的 Promise 实例,新的 Promise 实例的状态和值与调用 finally 的 Promise 实例一致
- 即使写了新的 return 语句也没有用
let pro = Promise.resolve('pro');
console.log("pro", pro); // pro Promise {<fulfilled>: 'pro'}
let proThen = pro.finally(_ => {
console.log("finally"); // finally
return Promise.resolve("finally"); // return 语句无效
}); // proThen Promise {<fulfilled>: 'pro'}
需要搞懂
① 改变 Promise 状态和执行回调函数,谁先执行
- 同步改变状态:先改变状态、后执行回调函数;异步改变状态:先执行回调函数,后改变状态
let pro1 = Promise.resolve();
let proThen = pro1.then(_ => { // 执行 then 方法属于异步操作
console.log("pro1 resolve"); // pro1 resolve
return "then";
});
console.log("proThen", proThen); // proThen Promise {<pending>}
如果直接在 then 方法后面打印 proThen
,得到的是 proThen Promise {<pending>}
;
如果在控制台打印 proThen
,得到的是 Promise {<fulfilled>: 'then'}
;
这是因为,Promise 实例的状态是通过异步操作改变的,无法通过同步操作获取改变后的状态
- 此时可以通过异步操作获取改变后的状态:
let pro1 = Promise.resolve();
let proThen = pro1.then(_ => {
console.log("pro1 resolve"); // pro1 resolve
return "then";
});
setTimeout(() => {
console.log("proThen", proThen); // proThen Promise {<fulfilled>: 'then'}
}, 0);
② 多个回调的执行
- 如果给 Promise 实例制订了多个回调函数,会按顺序执行所有的回调函数
let pro = Promise.resolve('data');
pro.then(value => {
console.log(value + 1); // data1
});
pro.then(value => {
console.log(value + 2); // data2
});
③ 链式调用
- 因为
then
/catch
/finally
返回的还是一个Promise
实例,所以可以链式调用
let pro = Promise.resolve('first');
pro.then(val1 => {
console.log("val1", val1); // val1 first
return "second";
}).then(val2 => {
console.log("val2", val2); // val2 second
}).then(val3 => {
console.log("val3", val3); // val3 undefined
});
上例中,第 1 个 then
返回了 "second"
,所以 val2
为 second
;第 2 个 then
没有返回值,所以 val3
为 undefined
let pro = Promise.reject('first');
pro.then(val1 => {
console.log("val1", val1);
return "second";
}).catch(reason => {
console.log("reason", reason); // reason first
}).then(val2 => {
console.log("val2", val2); // val2 undefined
});
上例中,因为 pro
的状态是 rejected,所以跳过第一个 then
方法,直接跳到了 catch
方法执行;
因为 catch
方法没有 return 语句,相当于 return Promise.resolve()
,所以 val2
为 undefined
- 在链式调用中,没有按照规则传入回调函数的 then、catch、finally 方法,都会被直接跳过:
let p1 = Promise.resolve('p1');
p1.then(res1 => {
console.log("res1", res1); // res1 p1
}).then(res2 => {
console.log("res2", res2); // res2 undefined
return 'string';
}).then().then('我是字符串').then(res3 => { // 这里有两个没写回调函数的 then 方法,会被直接跳过
console.log("res3", res3); // res3 string // 这里 res3 接收的是前面的合法返回值
});
④ Promise 异常穿透
使用 Promise 的链式调用时,可以在最后设置 catch
方法,并指定失败的回调函数
这样,前面任何操作出了异常,都会传到最后的 catch
中,执行其回调函数
let pro = Promise.resolve('pro');
pro.then(val1 => {
console.log("val1", val1); // val1 pro
return Promise.reject("reject")
}).then(val2 => {
console.log("val2", val2);
return "then2' return";
}).catch(reason => {
console.log("reason", reason); // reason reject
});
上例中,因为第一个 then
方法返回的是 rejected 状态的 Promise 实例,所以不会走后面的 then
方法
这样,就直接跳到了最后的 catch
方法中
let pro = Promise.resolve('ok');
pro.then(val1 => {
console.log("val1", val1); // val1 ok
throw 'error';
}).then(val2 => {
console.log("val2", val2);
}).catch(reason => {
console.log("reason", reason); // reason error
});
上例中,因为第 1 个 then
方法抛出了错误,所以不会执行第二个 then
方法,直接跳到了最后的 catch
方法
⑤ 中断 Promise 链
- 中断 Promise 链,就是说在使用
then
的链式调用时,在中间断开,不再执行后面的回调函数 - 办法:在回调函数中,返回一个 pendding 状态的 Promise 对象
let pro = Promise.resolve('ok');
pro.then(val1 => {
console.log("val1", val1); // val1 ok
return new Promise(() => {});
}).then(val2 => {
console.log("val2", val2);
}).catch(reason => {
console.log("reason", reason);
});
上述代码中,因为第 1 个 then
返回 pendding 状态的 Promise 对象,所以 Promise 链没有继续往下执行
all
& race
& allSettled
Promise.all(proArr)
- 等最慢的接⼝返回数据后,⼀起得到所有接口的数据
- 返回新的 Promise 实例
proArr
:Promise 实例对象组成的数组proArr
中所有的 Promise 实例的状态都为 fulfilled,则返回状态为 fulfilled 的 Promise 实例
此时该实例的值为:proArr
中所有 Promise 实例的值组成的数组
let p1 = new Promise(resolve => {
setTimeout(() => {
resolve('resolve p1');
}, 500);
});
let p2 = new Promise(resolve => {
setTimeout(() => {
resolve('resolve p2');
}, 1000);
});
let p3 = new Promise(resolve => {
setTimeout(() => {
resolve('resolve p3');
}, 1500);
});
Promise.all([p1, p3, p2]).then(res => {
console.log("res", res); // res ['resolve p1', 'resolve p2', 'resolve p3'] ( 1.5s 后输出 )
});
- 如果
proArr
中存在状态为 rejected 的 Promise 实例,则返回状态为 rejected 的 Promise 实例
此时该实例值为:proArr
中,第 1 个状态为 rejected 的 Promise 实例的值
let p1 = new Promise(resolve => {
setTimeout(() => {
resolve('resolve p1');
}, 1000);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('reject p2');
}, 2000);
});
let p3 = new Promise(resolve => {
setTimeout(() => {
reject('reject p3');
}, 10000);
});
Promise.all([p1, p3, p2]).catch(err => {
console.log("err", err); // err reject p2 ( 2s 后输出 )
});
Promise.allSettled(proArr)
-
等最慢的接⼝返回数据后,⼀起得到所有接口的数据
-
返回新的 Promise 实例
-
proArr
:Promise 实例对象组成的数组 -
不论
proArr
中 Promise 实例的状态,都返回状态为 fulfilled 的 Promise 实例
此时该实例的值为:proArr
中所有的 Promise 实例对象
let p1 = new Promise(resolve => {
setTimeout(() => {
resolve('resolve p1');
}, 1000);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('reject p2');
}, 2000);
});
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('reject p3');
}, 3000);
});
let pas = Promise.allSettled([p1, p2, p3]).then(valArr => {
valArr.forEach((item, index) => {
console.log(`item ${index+1}: `, item);
// item 1: {status: 'fulfilled', value: 'resolve p1'}
// item 2: {status: 'rejected', reason: 'reject p2'}
// item 3: {status: 'rejected', reason: 'reject p3'}
});
});
Promise.race(proArr)
- 等最块的接口返回数据后,得到该接口的数据
- 返回新的 Promise 实例
proArr
:Promise 实例组成的数组proArr
中,第一个改变 pendding 状态的 Promise 实例是什么状态,就返回什么状态的 Promise 实例
此时,返回的 Promise 实例的值为第 1 个改变 pendding 状态的 Promise 实例的值
let p1 = new Promise(resolve => {
setTimeout(() => {
resolve('resolve p1');
}, 500);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('reject p2');
}, 1000);
});
let p3 = new Promise(() => {
setTimeout(() => {
throw "error p3"; // 抛出异常
}, 0);
});
Promise.race([p1, p2, p3]).then(value => {
console.log("value", value); // value resolve p1
}).catch(reason => {
console.log("reason", reason);
});
- 抛出异常的 Promise 实例不会被 Promise.race 处理
- 上例中,第一个改变 pendding 状态的是
p1
;即使后面有 rejected 状态的 Promise 实例,也不会再处理
let p1 = new Promise(resolve => {
setTimeout(() => {
resolve('resolve p1');
}, 1500);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('reject p2');
}, 1000);
});
let p3 = new Promise(() => {
setTimeout(() => {
throw "error p3";
}, 500);
});
Promise.race([p1, p2, p3]).then(value => {
console.log("value", value);
}).catch(reason => {
console.log("reason", reason); // reason reject p2
});
- 上例中,第一个改变 pendding 状态的是
p2
案例
fs 配合 Promise 异步读取文件
const fs = require('fs');
// // ① 使用回调函数完成异步操作(异步操作的传统解决方案)
// fs.readFile('./demo.html', (err, data) => {
// if (err) throw err;
// console.log(data + '')
// });
// ② 使用 Promise 完成异步操作
let pro = new Promise((resolve, reject) => {
fs.readFile('./demo.html', (err, data) => {
if (err) reject(err);
resolve(data);
});
});
pro.then(value => {
console.log(value + '');
}, reason => {
console.log(reason);
});
- 将 fs 封装成 Promise 对象来使用
const fs = require('fs');
function myReadFile(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
myReadFile('./demo.html').then(value => { // 读取文件
console.log(value + '')
}, reason => {
console.log(reason);
});
每次都要自己封装,略显麻烦。util.promisify
应运而生
util.promisify
的作用:将函数封装成 Promise 实例
使用 util.promisify
将 fs 封装成 Promise 对象
const util = require('util');
const fs = require('fs');
let myReadFile = util.promisify(fs.readFile);
myReadFile('./demo.html').then(value => { // 读取文件
console.log(value + '')
}, reason => {
console.log(reason);
});
是不是超级方便!
使用 reduce 封装 Promise
function queue(numArr) {
numArr.reduce((promise, n) => {
return promise.then(_ => {
return new Promise(resolve => {
setTimeout(() => {
console.log(n);
resolve();
}, 1000);
});
});
}, Promise.resolve());
}
queue([1, 2, 3, 4, 5]);