写此篇文章的理由:
- 异步是高频应用场景,应该是无处不在
- 平常积累的知识比较零散,没有形成整套体系,所以专题化并付诸实践,掌握模棱两可的知识点
- 建立起思维、身体、情绪之间关系
- 每天充充电,生活质量才改变
看此文的收货:
- 懂得Promise和await/async的不容易注意到的知识点
- 经常用到的使用场景
进阶请看我的另一篇promise和await async总结
我觉得这2篇文章知识点有点重复,因为边总结边思考肯定有交集,所以我觉得文章质量:没有最好,只有更好。
有新的知识点后面继续补上,也欢迎指正。
此处说下心中一点小心思:输出高吸引力的文章,希望利人也利己,希望大家给个👍,不花钱哦!
一、Promise
写正文前,拿一个外网可以请求到的接口为例
https://api.github.com/users/github
function get(url, params, config = {}) {
return new Promise((resolve, reject) => {
axios.get(url, {
params,
...config,
}).then(res => {
resolve(res.data);
}).catch(err => {
reject(err.data);
})
});
}
后文请求得到的值用 【"真实请求到的值"】代替
1. 基本用法
1.1 p1的状态作为p2
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
p1是一个 Promise,3 秒之后变为rejected。p2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。
1.2 resolve后面的函数还会执行
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
1.3 resolve加上return,后面的不会再执行
new Promise((resolve, reject) => {
return resolve(1);
// 后面的语句不会执行
console.log(2);
})
一般来说,调用resolve或reject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolve或reject的后面。所以,最好在它们前面加上return语句,这样就不会有意外。
then方法返回的是一个新的Promise实例,因此可以采用链式方法。 第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
返回promise得到的值
get('https://api.github.com/users/github').then(res => {
console.log('first:',res) //"first:真实请求到的值"
return get('https://api.github.com/users/github');
}).then((res)=>{
console.log('second:',res) //"second:真实请求到的值"
})
直接返回一个值
//"first:真实请求到的值"
get('https://api.github.com/users/github').then(res => {
console.log('first:',res) //"first:真实请求到的值"
return 123;
}).then((res)=>{
console.log('second:',res) //"second:123"
})
2. Promise.prototype.then()
then方法是定义在原型对象Promise.prototype上的。作用是为 Promise 实例添加状态改变时的回调函数。
3. Promise.prototype.catch()
3.1 Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
get('https://api.github.com/users/github').then(res => {
throw new Error('error');
}).catch((res)=>{
console.log('second:',res) //second: Error: error
})
get('https://api.github.com/users/github').then(res => {
throw new Error('error');
}).then(null,err => {
console.log('err:',err) //err: Error: error
})
以下两种写法是等价的
// 写法一
const promise = new Promise(function(resolve, reject) {
try {
throw new Error('test'); // throw方式
} catch(e) {
reject(e);
}
});
promise.catch(function(error) {
console.log(error);
});
// 写法二
const promise = new Promise(function(resolve, reject) {
reject(new Error('test')); // reject方式
});
promise.catch(function(error) {
console.log(error);
});
3.2 如果 Promise 状态已经变成resolved,再抛出错误是无效的
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test'); //无效的
});
promise
.then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) });
// ok
3.3 Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获
get('https://api.github.com/users/github').then(res => {
console.log('first:',res) //"first:真实请求到的值"
return get('https://api.github.com/users/github');
}).then((res)=>{
console.log('second:',res) //"second:真实请求到的值"
})
上面代码中,一共有三个 Promise 对象:一个由get()产生,两个由then()产生。它们之中任何一个抛出的错误,都会被最后一个catch()捕获。
3.4 最好不要在then()方法里面定义 Reject 状态的回调函数,总是使用catch方法。
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
理由是:第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)
3.5 Promise 内部的错误不会影响到 Promise 外部的代码的执行官
通俗的说法就是“Promise 会吃掉错误”
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2); //运行到此行抛出错误,但是不会退出进程、终止脚本执行
});
};
someAsyncThing().then(function() {
console.log('everything is great');
});
setTimeout(() => { console.log(123) }, 2000); //依旧会执行
// Uncaught (in promise) ReferenceError: x is not defined
// 123
通过上面例子,所以用catch比较好
跟传统的try/catch代码块不同的是,如果没有使用catch()方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。
3.6 Promise 在下一轮“事件循环”再抛出错误,能捕获
const promise = new Promise(function (resolve, reject) {
resolve('ok');
setTimeout(function () { throw new Error('test') }, 0)
//在下一轮“事件循环”再抛出错误,会执行,错误会冒泡到外层,能捕获
});
promise.then(function (value) { console.log(value) });
// ok
// Uncaught Error: test
3.7 抛出错误在同一轮“事件循环”
页面出错,没有catch,没有输出错误信息(也就是没有捕获),因为resolved,再抛出错误是无效的
const promise = new Promise(function (resolve, reject) {
resolve('ok');
throw new Error('test')
});
promise.then(function (value) { console.log(value) });
//ok
这样也没有捕获
const promise = new Promise(function (resolve, reject) {
resolve('ok');
throw new Error('test')
});
promise
.then( (value)=> { console.log(value) })
.catch(e => {console.log('err:',e)});
//ok
如果没有resolve能够捕获
const promise = new Promise(function (resolve, reject) {
throw new Error('test')
});
promise
.then( (value)=> { console.log(value) })
.catch(e => {console.log('err:',e)});
//err: Error: test
3.8 一般总是建议,Promise 对象后面要跟catch()方法,可处理 Promise 内部发生的错误
catch()方法返回的还是一个 Promise 对象,因此后面还可以接着调用then()方法
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
someAsyncThing()
.catch(function(error) {
console.log('oh no', error);
})
.then(function() {
console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on
没错误跳过catch,直接执行后面的then()方法。此时,如果是then()方法里面报错,就与前面的catch()无关了
Promise.resolve()
.catch(function(error) {
console.log('oh no', error);
})
.then(function() {
console.log('carry on');
});
// carry on
3.9 catch还能再抛出错误
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
someAsyncThing().then(function() {
return someOtherAsyncThing();
}).catch(function(error) {
console.log('oh no', error);
// 下面一行会报错,因为 y 没有声明
y + 2;
}).catch(function(error) {
console.log('carry on', error);
});
// oh no [ReferenceError: x is not defined]
// carry on [ReferenceError: y is not defined]
第二个catch()方法是用来捕获前一个catch()方法抛出的错误
4. Promise.prototype.finally()
不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数
// resolve 的值是 undefined
Promise.resolve(2).then(() => {}, () => {})
// resolve 的值是 2
Promise.resolve(2).finally(() => {})
// reject 的值是 undefined
Promise.reject(3).then(() => {}, () => {})
// reject 的值是 3
Promise.reject(3).finally(() => {})
5. Promise.all()
const p = Promise.all([p1, p2, p3]);
5.1 参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例
5.2 参数中的所有状态都变成fulfilled,p就变成fulfilled,返回值组成数组
5.3 参数中的有一个被rejected,p就变成rejected
5.4 参数如果不是promise实例,会先调用Promise.resolve方法,将参数转为 Promise 实例
5.5 后面最好也跟着catch
5.6 如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]
只会报第一个错误
const p1 = new Promise((resolve, reject) => {
throw new Error('报错了1');
})
.then(result => result);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了2');
})
.then(result => result);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
//Error: 报错了1
6. Promise.race()
const p = Promise.race([p1, p2, p3]);
6.1 参数规则同Promise.all()
6.2 参数之中只要有一个实例率先改变状态,p的状态就跟着改变
6.3 应用场景:指定时间内没获取到结果,就报错
const p = Promise.race([
get('https://api.github.com/users/github'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error)
//5m内没得到结果,就报错
7. Promise.allSettled()
7.1 只有等到所有参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。
7.2 结果是数组,元素是对象,具体如下代码
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 }
// ],找出
可以通过status
7.3 应用场景:不关心异步操作的结果,只关心这些操作有没有结束
8. Promise.any()
8.1 只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态
8.2 如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态
8.3 跟Promise.race()方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected状态而结束
捕捉错误可以用
9. Promise.resolve()
9.1 可以将现有对象转为 Promise 对象
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
9.2 参数分为四种情况
(1) 参数是一个 Promise 实例
Promise.resolve将不做任何修改、原封不动地返回这个实例。
(2) 参数是一个thenable对象
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42
(3) 参数不是具有then方法的对象,或根本就不是对象
返回一个新的 Promise 对象,状态为resolved。
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
(4) 不带有任何参数
const p = Promise.resolve();
p.then(function () {
// ...
});
10. Promise.reject()
10.1 返回一个新的 Promise 实例,该实例的状态为rejected
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
10.2 传啥参数,啥参数作为reject的理由
注意:与Promise.resolve方法不一致的是:Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。
const thenable = {
then(resolve, reject) {
reject('出错了');
}
};
Promise.reject(thenable)
.catch(e => {
console.log(e === thenable)
})
// true
Promise.reject方法的参数是一个thenable对象,执行以后,后面catch方法的参数不是reject抛出的“出错了”这个字符串,而是thenable对象。
11. 实例
- 图片加载
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
- 异步请求
function get(url, params, config = {}) {
return new Promise((resolve, reject) => {
axios.get(url, {
params,
...config,
}).then(res => {
resolve(res.data);
}).catch(err => {
reject(err.data);
})
});
}
- 封装有正反按钮的组件,比如messageBox 确定:将promise断定为resolve状态 取消:将promise断定为reject状态
buttonAction() {
this.resolve();
}
二、await/async
1. 特点
- 内置执行器
- async函数返回的是 Promise 对象,Promise可以作为await命令的参数
- 比起星号和yield,更好的语义
- await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)
- await必须写在async函数中
- async函数内部return语句返回的值,会成为then方法回调函数的参数。
async function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
- 任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。
async function f() {
await Promise.reject('出错了');
await Promise.resolve('hello world'); // 不会执行
}
怎么能不中断呢?
方法一:把第一个await放在try...catch结构里面
async function f() {
try {
await Promise.reject('出错了');
} catch(e) {
}
return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
// hello world
方法二:await后面的 Promise 对象再跟一个catch方法,这样可以处理前面可能出现的错误
async function f() {
await Promise.reject('出错了')
.catch(e => console.log(e));
return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
// 出错了
// hello world
2. 函数写法
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)
// Class 的方法
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache1 = await this.cachePromise;
return name; //返回的值作为then中的参数
}
}
const storage = new Storage();
storage.getAvatar('jake').then(…);
// 箭头函数
const foo = async () => {};
3. await后面的参数
(1)如果不是 Promise 对象,就直接返回对应的值
async function f() {
// 等同于
// return 123;
return await 123;
}
f().then(v => console.log(v))
// 123
(2)await命令后面是一个thenable对象,会将其等同于 Promise 对象
class Sleep {
constructor(timeout) {
this.timeout = timeout;
}
then(resolve, reject) { //有then对象
const startTime = Date.now();
setTimeout(
() => resolve(Date.now() - startTime),
this.timeout
);
}
}
(async () => {
const sleepTime = await new Sleep(1000);//直接视为new Promise处理
console.log(sleepTime);
})();
// 1000
4. 错误处理机制
4.1 async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。
async function f() {
throw new Error('出错了');
//await new Promise(function (resolve, reject) {
// throw new Error('出错了');
//});
}
f().then(
v => console.log(v),
e => console.log(e)
)
// Error: 出错了
4.2 防止出错,使用try catch,这样也不会阻止下面运行
async function f() {
try {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
} catch(e) {
}
return await('hello world');
}
4.3 如果有多个await命令,可以统一放在一个try...catch结构中
为什么会有await/async await和promise的区别
catch捕捉错误
结合for...of一起使用
注意:能并发执行的不要继发运行,所以这也是它的缺点
Promise的缺点: 解决回调地狱的问题,
文章不在于快速输出,而在于打磨。
5. 注意点
5.1 最好把await命令放在try...catch代码块中或者await后面跟着catch
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}
// 另一种写法
async function myFunction() {
await somethingThatReturnsAPromise()
.catch(function (err) {
console.log(err);
});
}
5.2 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发,也就是可以不用await
5.3 await命令只能用在async函数之中,如果用在普通函数,就会报错
继发运行,可以用for...of
async function dbFuc(db) {
let docs = [{}, {}, {}];
for (let doc of docs) {
await db.post(doc);
}
}
最好不要用forEach,因为会并发
function dbFuc(db) { //这里不需要 async
let docs = [{}, {}, {}];
// 用forE爱吃,此写法是并发
docs.forEach(async function (doc) {
await db.post(doc); //await只能写在async函数之中
});
}
继发运行的另一种写法,可以用reduce
async function dbFuc(db) {
let docs = [{}, {}, {}];
await docs.reduce(async (_, doc) => {
await _;
await db.post(doc);
}, undefined);
}
如果确实希望多个请求并发执行,可以使用Promise.all方法
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) => db.post(doc));
let results = await Promise.all(promises);
console.log(results);
}
// 或者使用下面的写法
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) => db.post(doc));
let results = [];
for (let promise of promises) {
results.push(await promise);
}
console.log(results);
}
5.4 async 函数可以保留运行堆栈
const a = async () => {
await b();
c();
};
b()运行的时候,a()是暂停执行,上下文环境都保存着。
一旦b()或c()报错,错误堆栈将包括a()。
const a = () => {
b().then(() => c());
};
函数a内部运行了一个异步任务b()。
当b()运行的时候,函数a()不会中断,而是继续执行。
等到b()运行结束,可能a()早就运行结束了,b()所在的上下文环境已经消失了。
如果b()或c()报错,错误堆栈将不包括a()。
6. async 函数的实现原理
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () { //Generator 函数
// ...
});
}
//自动执行器
function spawn(genF) {
return new Promise(function(resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
7. 与其他异步处理方法的比较
假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。
Promise
function chainAnimationsPromise(elem, animations) {
// 变量ret用来保存上一个动画的返回值
let ret = null;
// 新建一个空的Promise
let p = Promise.resolve();
// 使用then方法,添加所有动画
for(let anim of animations) {
p = p.then(function(val) {
ret = val;
return anim(elem); //返回的也是promise对象,注意这种写法思路
});
}
// 返回一个部署了错误捕捉机制的Promise
return p.catch(function(e) {
/* 忽略错误,继续执行 */
}).then(function() {
return ret;
});
}
Generator
function chainAnimationsGenerator(elem, animations) {
return spawn(function*() {
let ret = null;
try {
for(let anim of animations) {
ret = yield anim(elem);
}
} catch(e) {
/* 忽略错误,继续执行 */
}
return ret;
});
}
写法复杂
async
async function chainAnimationsAsync(elem, animations) {
let ret = null;
//async最好加上try catch
try {
for(let anim of animations) {
ret = await anim(elem);
}
} catch(e) {
/* 忽略错误,继续执行 */
}
return ret;
}
async 函数的实现最简洁,最符合语义,几乎没有语义不相关的代码。它将 Generator 写法中的自动执行器,改在语言层面提供,不暴露给用户,因此代码量最少。
8. 实例
- 按顺序完成异步操作
比如,依次远程读取一组 URL,然后按照读取的顺序输出结果
Promise联合reduce方式,继发发出远程请求,结果是按照顺序输出
function logInOrder(urls) {
// 远程读取所有URL
const textPromises = urls.map(url => {
return fetch(url).then(response => response.text());
});
// 按次序输出
textPromises.reduce((chain, textPromise) => {
return chain.then(() => textPromise)
.then(text => console.log(text));
}, Promise.resolve());
}
await联合for...of方式,继发发出远程请求,结果是按照顺序输出
async function logInOrder(urls) {
for (const url of urls) {
const response = await fetch(url);
console.log(await response.text());
}
}
并发发出远程请求,结果是按照顺序输出
async function logInOrder(urls) {
// 并发读取远程URL
const textPromises = urls.map(async url => {
const response = await fetch(url);
return response.text();
});
// 按次序输出
for (const textPromise of textPromises) {
console.log(await textPromise);
}
}
map遍历异步,不能用forEach
async handleQrCode (ids) {
...
let promises = list.map(async (item, i) => {
return {
equipQrcodeFullPath: await formatFileUrl(images[i]),
}
})
this.qrCodeEquips = await Promise.all(promises); //得到数组
}
补充加强
-
使用场景:动画(减少嵌套)、错误捕捉(统一地方处理)、异步请求、img或者srcipt加载监听onload和onerror
-
Promise.then也是一个Promise
const p1 = new Promise((resolve, reject)=>{
resolve('success')
})
const p2 = p1.then((value)=>{console.log(value)}, (reason)=>{console.log(reason)})
//不加setTimeout
console.log(p1) //Promise{<resolve>:'success'} resolve状态的Promise
console.log(p2) //Promise{<pending>} pending状态的Promise
因为p2是微任务还没有执行,所以是准备状态
//加上setTimeout
setTimeout(()=>{
console.log(p1) //Promise{<resolve>:'success'} resolve状态的Promise
console.log(p2) //Promise{<resolve>:undefined} resolve状态的Promise
})
因为先轮训微任务,再轮训宏任务,当轮训微任务的时候,状态发生改变,
- then方法的写法:
class中的then方法会包装成promise
class A{
then(resolve,reject){
resolve() //此处加了改变状态,hhy才会被打印出来
}
}
async function get(){
await new A();
console.log('hhy')//hhy
}
get()
- class中静态方法
class {
static then(resolve,reject){}
}
3.对象的then方法
{
then(resolve, reject){
resolve('这是对象')
}
}
- then中return 一个Promise,后面的then是对这个新的Promise的处理,平时可以这么使用,这样不会嵌套太深
const p1 = new Promise((resolve, reject)=>{
resolve('success')
}).then((value)=>{
console.log(value)
// then中return 一个Promise,后面的then是对这个新的Promise的处理,平时可以这么使用,这样不会嵌套太深
1、 直接new Promise
return new Promise((resolve, reject)=>{
resolve('success2')})
//因为then也是一个Promise,所以也可以返回then
2. then对象
return {
then(resolve, reject){
resolve('这是对象')
}
}
3. class中含有then函数
class A{
then(resolve,reject){
resolve('这是对象')
}
}
return new A()
4. class静态方法
return class{
static then(resolve,reject){
resolve('这是对象')
}
}
}, (reason)=>{
console.log(reason)
}).then((res)=>{
console.log(res) //success2
})
catch异步处理是resolved状态
,所以使用Promise.all([A,B]),如果A使用了catch,不管前面是reject还是resolved,Promise.all就能执行正确,A Promise是reject,结果就是[undefined,resB]
const A = new Promise((resolve, reject)=>{
setTimeout(()=>{
reject('fail')
})
}).catch((error)=>{console.log(error)}) // catch异步处理是resolved状态
const B = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('success')
})
}).catch((error)=>{console.log(error)})
Promise.all([A,B]).then(res=>{
console.log(res) // [undefined, success]
}).catch((error)=>{console.log(error)})
map 多个异步,可以使用Promise.all,这个不是顺序执行
let nums = [1,2,3,4,5]
let promises = nums.map((item,i)=>{
return new Promise(resolve => {
setTimeout(()=>{
console.log(item) //1,2,3,4,5
resolve(item)
},1000*Math.random(i))
})
})
Promise.all(promises).then((res)=>{
console.log(res) //不知道顺序
})
- 队列执行
队列:按顺序执行,它的原理就是每次then返回新的Promise,使用了Promise.resolve()创建promise,promise执行一次更新一次
//map封装Promise队列
function queue(nums){
let promise = Promise.resolve();
nums.map((item)=>{
//关键:promise需要一直更新
promise = promise.then(()=>{
//这是原理:每次then返回一个新的Promise
return new Promise(resolve => {
setTimeout(()=>{
console.log(item) //1,2,3,4,5
resolve()
},1000)
})
})
})
}
queue([1,2,3,4,5])
//reduce封装Promise队列
function queue(nums){
nums.reduce((promise,item)=>{
//关键:promise需要一直更新
promise = promise.then(()=>{
//这是原理:每次then返回一个新的Promise
return new Promise(resolve => {
setTimeout(()=>{
console.log(item) //1,2,3,4,5
resolve()
},1000)
})
})
},Promise.resolve())
}
queue([1,2,3,4,5])
- await并行执行方式:
- Promise.all
//p1和p2都是返回Promise
async function test(){
let res = await Promise.all([p1,p2])
}
- 执行的时候不加await,获取数据加await
//p1和p2都是返回Promise
async function test(){
let h1 = p1();
let h2 = p2();
let h1val = await h1();
let h2val = await h2();
}
扩展阅读: