Appearance
Promise的使用
为何会有Promise?
根据《深入理解JS事件循环机制》这篇文章知道,Promise、Generator、async/await都是为了实现异步编程 传统方式是通过回调函数、事件监听、发布订阅模式实现的,Promise的出现大大丰富了异步编程的实现。
Promise状态转换自动机
Promise的状态转换是不可逆的,当pending转换为fulfilled或rejected时,只能通过.then来获取值再返回新的Promise对象
由pending状态转换为fulfilled或rejected状态时才放入到微任务队列,执行.then就是将该任务从微任务队列取出并执行。
Promise的常规使用
承诺-兑现承诺结构
承诺: resolve(value):如果任务成功完成并带有结果 value。 reject(error):如果出现了 error,error 即为 error 对象。
javascript
let promise = new Promise(function(resolve, reject) {
// executor(生产者代码)
});new Promise(arg)里面的参数arg是生成者执行的函数 兑现承诺: .then():
.then的第一个参数是一个函数,该函数将在promise resolved后运行并接收结果。.then的第二个参数也是一个函数,该函数将在promise rejected后运行并接收error。
.catch():捕捉错误 .finally():最后执行
例子
如下面这个加载脚本的函数,加载过程较慢,因此需要将它封装成异步函数 使用回调函数的形式:
javascript
function loadScript(src, callback) {
// 创建DOM结点
let script = document.createElement('script');
script.src = src;
// onload事件在资源被加载完成后会被触发
script.onload = () => {
callback(script);
}
// 插入DOM结点
document.head.append(script);
}
loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => {
console.log(`Cool, the script ${script.src} is loaded`);
});调用loadScript函数,传入src和一个回调函数callback,在loadScript内部调用箭头函数,打印script.src信息 使用Promise形式:
javascript
function loadScript(src) {
return new Promise(function (resolve, reject) {
let script = document.createElement('script');
script.src = src;
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Script load error for ${src}`));
document.head.append(script);
});
}
let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js");
promise.then(
script => alert(`${script.src} is loaded!`),
error => alert(`Error: ${error.message}`)
);
promise.then(script => alert('Another handler...'));调用loadScript函数,传入src,返回一个Promise对象,资源加载完成后触发onload函数,resolve(script)加载失败,然后执行到reject(),兑现承诺结构中获取错误信息,执行error => {}
Promise链条

Promise API
.all和.race
相同点:
- API内的传入promise数组的执行逻辑是相同的
- 返回值都是一个fulfilled状态的promise对象
- 如果报错,都是执行.catch。但报错不影响对promise数组每项的执行。
不同点:
Promise.all是与逻辑,只有全部为resolve才会执行.then,.then传入的参数是所有resolve结果构成的数组Promise.race是或逻辑,有一个reslove就会执行.then,.then传入的参数是当前resolve结果
下面举例说明:
js
function fn1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('fn1');
resolve(1);
}, 2000);
})
}
function fn2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('fn2');
resolve(2);
}, 0);
})
}
function fn3() {
return new Promise((resolve, reject) => {
console.log('fn3');
reject(3);
})
}
let p = Promise.all([fn2(), fn1(), fn3()]);
p.then(
res => { console.log('all.res = ' + res); }
).catch(
err => { console.log('all.err = ' + err); }
)
/*
fn3
all.err = 3
fn2
fn1
*/- 调用fn2(),加入到宏任务队列;调用f1(),加入到宏任务队列;调用fn3()就打印输出fn3
- fn3()中执行reject(),将该项的promise状态修改为rejected,因此错误被捕获,执行catch部分
- 执行宏任务队列中的任务,由于f2定时器时间短,因此先取出f2()任务执行,将该项的promise状态修改为fulfilled;同理再次处理f1()
.allSettled
Promise.allSettled(args,callback)是等到所有Promise都结束后才使用其回调函数
还是上面的例子,改成.allSettled:
javascript
let p = Promise.allSettled([fn2(), fn1(), fn3()]);
p.then(
res => { console.log(res); }
).catch(
err => { console.log('all.err = ' + err); }
)
/*
fn3
fn2
fn1
[
{ status: 'fulfilled', value: 2 },
{ status: 'fulfilled', value: 1 },
{ status: 'rejected', reason: 3 }
]
*/Promisification
它指将一个接受回调的函数转换为一个返回 promise 的函数。
javascript
// promisify(f, true) 来获取结果数组
function promisify(f, manyArgs = false) {
return function (...args) {
return new Promise((resolve, reject) => {
function callback(err, ...results) { // 我们自定义的 f 的回调
if (err) {
reject(err);
} else {
// 如果 manyArgs 被指定,则使用所有回调的结果 resolve
resolve(manyArgs ? results : results[0]);
}
}
args.push(callback);
f.call(this, ...args);
});
};
}
// 用法:
f = promisify(f, true);
f(...).then(arrayOfResults => ..., err => ...);async/await
async:用来修饰函数,使该函数异步执行,不阻碍后续函数的执行,让这个函数总是返回一个promise对象。
- 如果返回的是字符串等非promise对象,或者没有返回值,则被包装成fulfilled状态的promise对象
- 如果异常,则被包装成rejected状态的对象
当然也可以手动设置为pending状态:
js
async function fn(){
return new Promise((resolve,reject)=>{
console.log('kkk');
})
}await:
- 限定:只能放在async中,await后面只能跟promise对象。
- 阻塞条件:跟着的promise对象只有为fulfilled或rejected类型才会继续执行,否则就阻塞在这里
- 返回值:通过await修饰后可以直接获取promise对象中的value或error,如果是异常try…catch捕获即可
执行async函数返回的promise对象会加入到微任务队列中,等用await处理时才会从队列中取出执行
js
async function fn() {
return new Promise((resolve, reject) => {
console.log('async fn');
// resolve('async fn')
})
}
async function fn2() {
let val = await fn();
// 这里不会执行,因为fn()返回的promise状态为pending,会发生阻塞
console.log('返回值:' + val);
}
fn2();