# Promise
promise是一个构造函数,用来封装一个异步操作并可以获取其结果。
new Promise( function(resolve, reject) {...});
$(document).ready(function(){
//获取第一页数据
$.getJSON("json/poster.json?page=1",function(result){
attachPoster(result);
//获取第二页数据
$.getJSON("json/poster.json?page=2",function(result){
attachPoster(result);
...
});
});
});
这叫做"回调地狱"。上面的例子,改写:
function getPoster(page){
const promise = new Promise(function(resolve,reject){
$.getJSON("json/poster.json?page="+page,function(result){
resolve(result);
})
});
return promise;
}
getPoster(1).then(function(result){//获取第一页
attachPoster(result);
return getPoster(2);
}).then(function(result){//获取二页
attachPoster(result);
return getPoster(3);
}).then(funciton(result){//获取第三页 ...})
比层层嵌套更清晰,更符合逻辑。Promise就是 解决回调函数嵌套 的一种方案。
new Promise((resolve, reject) => {
resolve({
name: "第1个传递的值"
});
}).then((result) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(result)
resolve({
name: "第2个传递的值"
})
}, 200)
})
}).then((result) => {
console.log(result)
return 2020
}).then((result) => {
console.log(result)
})
//{name: "第1个传递的值"}
//{name: "第2个传递的值"}
//2020
如果then是个promise则把resolve/reject传给下个then如果没有promise,则会把return值给下一个thende的第一个函数,如果没有return则下一个then接受为undefined
# then与resolve
then的入参函数,就是resovle的回调方法。相当于callback作为入参的回调,只不过用了then的属性方法传入的。如嵌套多层,then的链式调用就发挥巨大优先性了,它能把层层嵌套平铺开。
# 幂等性 Idempotent
Promise.resolve是个幂等方法,而Promise.reject不是
foo(x) 将产生与 foo(foo(x))、foo(foo(foo(x))) 等相同的输出
let p = Promise.resolve(7);
setTimeout(console.log, 0, p === Promise.resolve(p));
// true
setTimeout(console.log, 0, p === Promise.resolve(Promise.resolve(p)));
// true
GET /pageX HTTP/1.1是幂等的。连续调用多次,客户端接收到的结果都是一样的:
GET /pageX HTTP/1.1
POST /add_row HTTP/1.1不是幂等的。如果调用多次,就会增加多行记录:
POST /add_row HTTP/1.1
POST /add_row HTTP/1.1 -> Adds a 2nd row
DELETE /idX/delete HTTP/1.1是幂等的,即便是不同请求之间接收到的状态码不一样:
DELETE /idX/delete HTTP/1.1 -> Returns 200 if idX exists
DELETE /idX/delete HTTP/1.1 -> Returns 404 as it just got deleted
# reject
const promise = new Promise(function(resolve,reject){
somethingDO();
if (/*结果符合预期,异步操作成功*/) {
resolve()
}else/*不符合预期,操作失败*/
{
reject();
}
})
Pomise有三种状态,分别是pending(进行中),resolved(已成功),rejected(已失败),一旦达到相应的状态,就会回调相应的方法。其实称作已成功,或者已失败并不准确,ES6中标准说法fullfiled,rejected。
对应的,then方法有两个入参,分别实现resolved,rejected的回调方法。
promise.then(function(value) {
// resolved
}, function(error) {
// rejected
});
继续上面的实例,我们在方法增加控制,生成一个1-10的随机方法,如果大于5就表示失败。
function getPObj(num){
var p = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("开始执行定时器:"+num);
var i = Math.ceil(Math.random()*10); //生成1-10的随机数
if (i<5) {
resolve(num);
}else{
reject(num);
}
},2000);
});
return p;
}
getPObj(1).then(function(data){
console.log("执行回调方法:"+data);
return getPObj(2);
},function(data){
console.log("执行回调方法失败:"+data);
}).then(function(data){
console.log("执行回调方法:"+data);
return getPObj(3);
},function(data){
console.log("执行回调方法失败:"+data);
}).then(function(data){
console.log("执行回调方法:"+data);
},function(data){
console.log("执行回调方法失败:"+data);
});
如果返回失败,我们希望终止掉整个链条,但是从实际结果看,是继续往下执行。这是因为,回调第一个reject的方法后,没有返回值,Promise会自动返回一个undefined,传入下一个链条的resolve方法中,并继续后面的then链。
# catch
try...catch是常用捕获异常的方法,在promise对象中也有catch的方法。用来捕获then回调方法中抛出的各类异常,用法如下:
p.then(function(){
...
}).catch(e){
....
}
catch方法实际就是实现了全局的reject方法。实际开发,一般采用catch代替reject。
# finally
try...catch...finally是黄金组合。finally表示无论什么状态,必定都会执行。
function getPObj(num){
var p = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("开始执行定时器:"+num);
var i = Math.ceil(Math.random()*10); //生成1-10的随机数
if (i<5) {
resolve(num);
}else{
reject(new Error("出错了"));
}
},2000);
});
return p;
}
getPObj(1).then(function(data){
console.log("执行回调方法:"+data);
return getPObj(2);
}).then(function(data){
console.log("执行回调方法:"+data);
return getPObj(3);
}).then(function(data){
console.log("执行回调方法:"+data);
}).catch(function(e){
console.log(e);
}).finally(function(){
console.log("finally");
});
# all
Promise.all可以将几个Promise对象封装成一个,格式如下:
Promise.all([p1,p2,p3]).then(function(data){...})
当这几个对象都变成resolved状态后,总状态变为resolved;否则,其中有一个为rejected状态,则变成reject,其他的可以忽略。可以理解为p1&&p2&&p3。
那返回的data是什么样子, 如果是resolved状态,则是各个对象data的组合;如果是rejected,则是第一个到达rejected状态返回的data值。 以例为证。
function getPObj(num){
var p = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("开始执行定时器:"+num);
if((Math.random()*10)>3){
resolve(num);
}else{
reject(num)
}
},2000);
});
return p;
}
Promise.all([getPObj(1),getPObj(2),getPObj(3)]).then(function(data){
console.log("resolve");
console.log(data);
}).catch(function(e){
console.log("error");
console.log(e);
})
执行的结果:
//概率问题
开始执行定时器:1
error
1
开始执行定时器:2
开始执行定时器:3
--------------------------------
开始执行定时器:1
开始执行定时器:2
开始执行定时器:3
resolve
(3) [1, 2, 3]
const timer = a => {
return new Promise(res =>
setTimeout(() => {
res(a);
}, Math.random() * 100)
);
};
const all = Promise.all([
timer('first'),
timer('second')
]).then(data => console.log(data));
//["first", "second"]
Promise解决的顺序与 Promise.all 无关。可以可靠地指望它们以数组参数中提供的相同顺序返回。
# race
Promise.race([p1,p2,p3]).then(function(data){...})
与all不同的是,看谁执行的快,then就回到回调谁的结果。可以理解为p1||p2||p3
看实例:
function getPObj(num){
var p = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("开始执行定时器:"+num);
var i = Math.ceil(Math.random()*10); //生成1-10的随机数
if (i<5) {
resolve(num);
}else{
reject(num);
}
},2000);
});
return p;
}
Promise.race([getPObj(1),getPObj(2),getPObj(3)]).then(function(data){
console.log("resolve");
console.log(data);
}).catch(function(e){
console.log("error");
console.log(e);
})
执行结果(由于随机数,各位执行的结果会有不一样的情况):
开始执行定时器:1
resolve
1
开始执行定时器:3
----------------------------------
开始执行定时器:1
error
开始执行定时器:2
开始执行定时器:3
当接受到第一个对象的resolved状态后,其他的两个抛弃处理。
# trycatch与promise
try {
throw new Error('foo');
} catch(e) {
console.log(e); // Error: foo
}
try {
Promise.reject(new Error('bar'));
console.log('1')
} catch(e) {
//promise的抛出的reject不走这里
console.log(e);
console.log('2')
}
// Error: foo
// 1
// Uncaught (in promise) Error: bar
- try 的 catch 和 promise 的 catch 有什么区别?
一、相同点,都是获取代码错误信息的方法,而且都不能获取异步错误
- try… catch…
try {
console.log(a)
} catch (error) {
console.log(error) // ReferenceError: a is not defined
}
try {
setTimeout(() => {
console.log(a)
}, 100)
} catch (error) {
console.log(error)
}
// 浏览器报错:Uncaught ReferenceError: a is not defined
- Promise… catch…
new Promise((resolve, reject) => {
console.log(a)
}).catch(error => {
console.log(error) // ReferenceError: a is not defined
})
new Promise((resolve, reject) => {
setTimeout(() => {
console.log(a)
}, 100)
}).catch(error => {
console.log(error)
})
// 浏览器报错:Uncaught ReferenceError: a is not defined
二、不同点:try… catch… 不能捕获 Promise 里 reject 出来的错误信息
try {
Promise.reject('出错了');
} catch(e) {
console.log(e)
}
// 浏览器报错:Uncaught (in promise) 出错了
Promise 里 reject 出来的错误信息,可用 Promise.catch() 的方法来获取
try {
Promise.reject('出错了')
.catch(err => { console.log('2', err) });
console.log(a);
} catch (error) {
console.log(error)
}
// ReferenceError: a is not defined
// 2 出错了
能被 try catch 捕捉到的异常,必须是在报错的时候,线程执行已经进入 try catch 代码块,且处在 try catch 里面,这个时候才能被捕捉到。
try...catch (opens new window)
# 实例
new Promise((resolve,reject)=>{
// resolve(1000)
reject(10)
})
// //不注释的话,下面的catch都会不走,resolve1的res为undefined
// .then(res=>{
// console.log('第一个方法resolve',res)
// },res=>{
// console.log('第二个方法reject',res)
// })
.catch(e=>{
console.log('reject1',e)
return Promise.resolve(7777)
}).catch(e=>{
console.log('reject2',e)
return Promise.resolve(8888)
}).finally(e=>{
console.log('finally1',e)
})
.then(res=>{
console.log('resolve1',res)
}).finally(e=>{
console.log('finally2',e)
})
// reject1 10
// finally1 undefined
// resolve1 7777
// finally2 undefined
# $.ajax与promise
jquery封装的ajax本身就是最终结果就是一个promise,经过测试,在老版本中不支持catch模式推出异常,但是支持在then的第二个函数抛出错误,比较新的版本如3.x版本是支持catch报异常!
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>ajax</title>
<style type="text/css">
</style>
</head>
<body>
<!-- <script src="https://libs.baidu.com/jquery/1.11.1/jquery.min.js"></script> -->
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<!-- <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> -->
<script type="text/javascript">
var val=$.ajax({
url:'https://douban.uieee.com/v2/movie/in_theaters',
})
val.then(res=>{
console.log(res)
},err=>{
console.log(`err`)
console.log(err)
})
val.then(res=>{
console.log(res)
}).catch((err)=>{
console.log(`err`)
console.log(err)
})
</script>
</body>
</html>
var val=$.ajax({
url:'https://douban.uieee.com/v2/movie/in_theaters',
})
var val1=$.ajax({
url:'https://douban.uieee.com/v2/movie/coming_soon',
})
Promise.all([val,val1]).then(res=>{
console.log(res)
// (2) [{…}, {…}]
// 0: {count: 20, start: 0, total: 62, subjects: Array(20), title: "正在上映的电影-北京"}
// 1: {count: 20, start: 0, total: 45, subjects: Array(20), title: "即将上映的电影"}
// length: 2
// __proto__: Array(0)
}).catch(err=>{
console.log(`err`)
console.log(err)
})
# async/await异步函数
async 和 await 可以说是异步终极解决方案了,相比直接用 Promise ,可以用同步的方式写异步的回调,也不需要.then这种形式。
async函数返回值 :一个Promise,这个promise要么会通过一个由async函数返回的值被解决,要么会通过一个从async函数中抛出的(或其中没有被捕获到的)异常被拒绝。
return 的值会被Promise.resolve()包装成promise对象
async function fun(){
}
let s = fun()
console.log(s)//Promise {<fulfilled>: undefined}
async function fun1(){
return 10
}
let s1 =fun1()
console.log(s1)//Promise {<fulfilled>: 10}
async function foo() {
console.log(1);
return 3;
}
// Attach a resolved handler to the returned promise
foo().then(console.log);
console.log(2);
//1 2 3
当然,直接返回一个promise对象也是一样的
async function foo() {
console.log(1);
return Promise.resolve(3);
}
// Attach a resolved handler to the returned promise
foo().then(console.log);
console.log(2);
//1 2 3
异步函数的返回值期待(不是必须)一个实现了thenable的接口,但常规值也可以。如果返回的是实现thenable接口的对象,则这个对象可以由提供给then()的处理程序“解包”。如果不是,则返回值就被当做已经解决的promise
// Return a primitive
async function foo() {
return 'foo';
}
foo().then(console.log);
// foo
// Return a non-thenable object
async function bar() {
return ['bar'];
}
bar().then(console.log);
// ['bar']
// 实现一个thenable非期约函数
async function baz() {
const thenable = {
then(callback) { callback('baz'); }
};
return thenable;
}
baz().then(console.log);
// baz
// Return a promise
async function qux() {
return Promise.resolve('qux');
}
qux().then(console.log);
// qux
等待会抛出错误的同步操作,会返回拒绝的期约:
async function foo() {
console.log(1);
throw 3;
}
// Attach a rejected handler to the returned promise
foo().catch(console.log);
console.log(2);
// 1
// 2
// 3
async function foo() {
await Promise.reject(10)
}
// Attach a rejected handler to the returned promise
foo().catch(console.log);
console.log(1);
async function foo1() {
return Promise.reject(20)
}
// Attach a rejected handler to the returned promise
foo1().catch(console.log);
console.log(2);
// 1
// 2
// 10
// 20
单独的Promise.reject()不会被异步函数捕获,而会抛出未捕获错误。不过,对拒绝的期约使用await/return 则会释放错误值
async function foo() {
Promise.reject(10)
}
// Attach a rejected handler to the returned promise
foo().catch(console.log);
console.log(1);
// 1
// Uncaught (in promise) 10
async函数可能包含0个或者多个await表达式。await表达式会暂停整个async函数的执行进程并出让其控制权,只有当其等待的基于promise的异步操作被兑现或被拒绝之后才会恢复进程 。promise的解决值会被当作该await表达式的返回值。使用async / await关键字就可以在异步代码中使用普通的try / catch代码块。
async function test() {
// 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
// 如果有依赖性的话,其实就是解决回调地狱的例子了
await fetch(url)
await fetch(url1)
await fetch(url2)
}
function getSyncTime() {
return new Promise((resolve, reject) => {
try {
let startTime = new Date().getTime()
setTimeout(() => {
let endTime = new Date().getTime()
let data = endTime - startTime
resolve( data )
}, 500)
} catch ( err ) {
reject( err )
}
})
}
async function getSyncData() {
let time = await getSyncTime()
let data = `endTime - startTime = ${time}`
return data
}
async function getData() {
let data = await getSyncData()
console.log( data )
}
getData()
- 可以让异步逻辑用同步写法实现,当然,只是在这个async中,并不阻塞其他函数的执行
- 最底层的await返回需要是Promise对象
- 可以通过多层 async function 的同步写法代替传统的callback嵌套
let a = 0
let b = async () => {
a = a + await 10
console.log('2', a) // -> '2' 10
}
b()
a++
console.log('1', a) // -> '1' 1
//先 "1" 1再 "2" 10
首先函数 b 先执行,在执行到 await 10 之前变量 a 还是 0,因为 await 内部实现了 generator ,
generator 会保留堆栈中东西,所以这时候 a = 0 被保存了下来因为 await 是异步操作,
后来的表达式不返回 Promise 的话,就会包装成 Promise.reslove(返回值),然后会去执行函数外的同步代码
同步代码执行完毕后开始执行异步代码,将保存下来的值拿出来使用,这时候 a = 0 + 10
async函数一定会返回一个promise对象。如果一个async函数的返回值看起来不是promise,那么它将会被隐式地包装在一个promise中。
async function foo() {
return 1
}
等价以下代码
function foo() {
return Promise.resolve(1)
}
async函数的函数体可以被看作是由0个或者多个await表达式分割开来的。从第一行代码直到(并包括)第一个await表达式(如果有的话)都是同步运行的。这样的话,一个不含await表达式的async函数是会同步运行的。然而,如果函数体内有一个await表达式,async函数就一定会异步执行。
async function foo() {
await 1
}
等价以下代码
function foo() {
return Promise.resolve(1).then(() => undefined)
}
# await处理期约
await后面跟期约,为了执行异步函数,实际上会有两个任务被添加到消息对了中,结果如下(新版本浏览器因为TC39对期约情况处理做了修改:123458967)
async function foo() {
console.log(2);
console.log(await Promise.resolve(8));
console.log(9);
}
async function bar() {
console.log(4);
console.log(await 6);
console.log(7);
}
console.log(1);
foo();
console.log(3);
bar();
console.log(5);
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
await 后面可以跟任何的JS 表达式。虽然说 await 可以等很多类型的东西,但是它最主要的意图是用来等待 Promise 对象的状态被 resolved。
- 如果await后面是 promise对象会造成异步函数停止执行并且等待 promise 的解决,
- 如果await后面是 正常的表达式则立即执行。
function sleep(second) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(' enough sleep~');
}, second);
})
}
async function awaitDemo() {
let result = await sleep(2000);
console.log("123")
console.log(result);
}
awaitDemo();// 两秒之后会被打印出来 '123'和' enough sleep~'
function sleep(second) {
setTimeout(() => {
console.log(' enough sleep~');
}, second);
}
async function awaitDemo() {
let result = await sleep(2000);
console.log("123");
}
awaitDemo();//立即输出123 两秒后输出' enough sleep~'
# 异步扩展
需求:来实现一个「运动路径动画」流程:移动页面上元素 target先从原点出发,向左移动 20px,之后再向上移动 50px,最后再次向左移动 30px,请把运动动画实现出来。
将移动的过程封装成一个 walk 函数,该函数要接受以下三个参数。
- direction:字符串,表示移动方向,这里简化为「left」、「top」两种枚举
- distance:整型,可正或可负
- callback:动作执行后回调
direction 表示移动方向,distance 表示移动距离。通过 distance 的正负值,我们可以实现四个方向的移动。
- 回调地狱法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
</head>
<style type="text/css">
#man{
width:100px;
height:100px;
border:1px solid red;
}
</style>
<body>
<div id='man'></div>
</body>
</html>
<script type="text/javascript">
const target = document.querySelectorAll('#man')[0];
target.style.cssText = `
position: absolute;
left: 0px;
top: 0px
`;
const walk = (direction, distance, callback) => {
setTimeout(() => {
let currentLeft = parseInt(target.style.left, 10);
let currentTop = parseInt(target.style.top, 10);
const shouldFinish =
(direction === 'left' && currentLeft === -distance) ||
(direction === 'top' && currentTop === -distance);
if (shouldFinish) {
// 任务执行结束,执行下一个回调
callback && callback();
} else {
if (direction === 'left') {
currentLeft--;
target.style.left = `${currentLeft}px`;
} else if (direction === 'top') {
currentTop--;
target.style.top = `${currentTop}px`;
}
walk(direction, distance, callback);
}
}, 20);
};
walk('left', 20, () => {
walk('top', 50, () => {
walk('left', 30, Function.prototype);
});
});
</script>
- promise法
const target = document.querySelectorAll('#man')[0];
target.style.cssText = `
position: absolute;
left: 500px;
top: 500px
`;
const walk = (direction, distance) =>
new Promise((resolve, reject) => {
const innerWalk = () => {
setTimeout(() => {
let currentLeft = parseInt(target.style.left, 10);
let currentTop = parseInt(target.style.top, 10);
const shouldFinish =
(direction === 'left' && currentLeft === -distance) ||
(direction === 'top' && currentTop === -distance);
if (shouldFinish) {
// 任务执行结束
resolve();
} else {
if (direction === 'left') {
currentLeft--;
target.style.left = `${currentLeft}px`;
} else if (direction === 'top') {
currentTop--;
target.style.top = `${currentTop}px`;
}
innerWalk();
}
}, 20);
};
innerWalk();
});
walk('left', 20)
.then(() => walk('top', 50))
.then(() => walk('left', 30));
- generator 方案
const target = document.querySelectorAll('#man')[0];
target.style.cssText = `
position: absolute;
left: 0px;
top: 0px
`;
const walk = (direction, distance) =>
new Promise((resolve, reject) => {
const innerWalk = () => {
setTimeout(() => {
let currentLeft = parseInt(target.style.left, 10);
let currentTop = parseInt(target.style.top, 10);
const shouldFinish =
(direction === 'left' && currentLeft === -distance) ||
(direction === 'top' && currentTop === -distance);
if (shouldFinish) {
// 任务执行结束
resolve();
} else {
if (direction === 'left') {
currentLeft--;
target.style.left = `${currentLeft}px`;
} else if (direction === 'top') {
currentTop--;
target.style.top = `${currentTop}px`;
}
innerWalk();
}
}, 20);
};
innerWalk();
});
function* taskGenerator() {
yield walk('left', 20);
yield walk('top', 50);
yield walk('left', 30);
}
const gen = taskGenerator();
//我们定义了一个 taskGenerator 生成器函数,并实例化出 gen,手动执行:
gen.next() //将会向左偏移 20 像素。
// 再次手动执行:
gen.next() // 将会向上偏移 50 像素。
// 再次手动执行:
gen.next() //将会向左偏移 30 像素。
唯一的不便之处就是需要我们反复手动执行 gen.next()。为此社区上早有方案,kj 大神的 co 库 (opens new window),能够自动包裹 generator 并执行
- async/await方案
const target = document.querySelectorAll('#man')[0];
target.style.cssText = `
position: absolute;
left: 0px;
top: 0px
`;
const walk = (direction, distance) =>
new Promise((resolve, reject) => {
const innerWalk = () => {
setTimeout(() => {
let currentLeft = parseInt(target.style.left, 10);
let currentTop = parseInt(target.style.top, 10);
const shouldFinish =
(direction === 'left' && currentLeft === -distance) ||
(direction === 'top' && currentTop === -distance);
if (shouldFinish) {
// 任务执行结束
resolve();
} else {
if (direction === 'left') {
currentLeft--;
target.style.left = `${currentLeft}px`;
} else if (direction === 'top') {
currentTop--;
target.style.top = `${currentTop}px`;
}
innerWalk();
}
}, 20);
};
innerWalk();
});
const task = async function () {
await walk('left', 20);
await walk('top', 50);
await walk('left', 30);
};
task() // 只需要直接执行 task() 即可。
# 实用案例
请求图片进行预先加载,假设预先有 urlIds 数组,数组的每一项都可以按照规则拼接成一个完整的图片地址。根据这个数组,依次请求图片进行预加载。
const loadImg = urlId => {
const url = `https://www.image.com/${urlId}`
return new Promise((resolve, reject) => {
const img = new Image()
img.onerror = function() {
reject(urlId)
}
img.onload = function() {
resolve(urlId)
}
img.src = url
})
}
该方法进行 promise 化(promisify),在图片成功加载时进行 resolve,加载失败时 reject。
依次请求图片:
const urlIds = [1, 2, 3, 4, 5]
urlIds.reduce((prevPromise, urlId) => {
return prevPromise.then(() => loadImg(urlId))
}, Promise.resolve())
使用了数组 reduce 方法,当然也可以面向过程实现:
const loadImgOneByOne = index => {
const length = urlIds.length
loadImg(urlIds[index]).then(() => {
if (index === length - 1) {
return
}
else {
loadImgOneByOne(++index)
}
})
}
loadImgOneByOne(0)
也可以采用 async/await 实现
const loadImgOneByOne = async () => {
for (i of urlIds) {
await loadImg(urlIds[i])
}
}
loadImgOneByOne()
上述代码的请求都是依次执行的,只有成功加载完第一张图片,才继续进行下一张图片的加载。
如果要求提高效率,将所有图片的请求一次性发出,该如何做呢?
const urlIds = [1, 2, 3, 4, 5]
const promiseArray = urlIds.map(urlId => loadImg(urlId))
Promise.all(promiseArray)
.then(() => {
console.log('finish load all')
})
.catch(() => {
console.log('promise all catch')
})
# promise
- 没有执行resolve和reject情况下,不会进入then
new Promise((resolve,reject)=>{
console.log('promise')
}).then((res)=>{
console.log('then1.1',res)
})
//promise
- 没有执行resolve和reject,人为抛出错误,如果有catch处理异常,会进入catch,没有catch,代码报错
new Promise((resolve,reject)=>{
console.log('promise')
try{
asdhiasfd
}catch(e){
ewqeeqwe
}
// throw new Error('ewqeeqwe');
}).then((res)=>{
console.log('then1.1',res)
}).catch(e=>{
console.log(e)
})
//promise
// ReferenceError: ewqeeqwe is not defined
// at 000.html:25:3
// at new Promise (<anonymous>)
// at 000.html:20:1
- 处理异常(手动或者reject),如果then的第二个函数存在,catch将不会接受到异常错误
new Promise((resolve,reject)=>{
console.log('promise')
throw new Error('手动错误');
//reject('xxx')
}).then((res)=>{
console.log('then1.1',res)
},(reject)=>{
console.log('then1.2',reject)
}).catch(e=>{
console.log('catch捕捉',e)
})
//promise
// then1.2 Error: 手动错误
// at 000.html:23:8
// at new Promise (<anonymous>)
// at 000.html:20:1
- catch后的流程,如果catch未执行,取catch前面执行的返回值
new Promise((resolve,reject)=>{
console.log('promise')
reject(9999)
}).then((res)=>{
console.log('then1.1',res)
},(reject)=>{
console.log('then1.2',reject)
return 100
}).catch(e=>{
console.log('catch捕捉',e)
return 1000
}).then((res)=>{
console.log('catch后面的res',res)
})
//promise
//then1.2 9999
//catch后面的res 100
- promise没有reject函数处理,走进catch,catch返回值会传给后面的then
new Promise((resolve,reject)=>{
console.log('promise')
reject(9999)
}).then((res)=>{
console.log('then1.1',res)
}).catch(e=>{
console.log('catch捕捉',e)
return 1000
}).then((res)=>{
console.log('catch后面的res',res)
})
//promise
// catch捕捉 9999
// catch后面的res 1000
- 如果有多个catch,被第一个catch捕获后,后续的就不会再捕获这个错误,除非,错误是第一个catch之后;同时resolve()也可能走catch,出错的情况
new Promise((resolve,reject)=>{
resolve(action)
}).then((res)=>{
console.log('then1.1',res)
}).catch(e=>{
console.log('catch捕捉',e)
}).then((res)=>{
console.log('catch后面的res',res)
}).catch(e=>{
console.log('第二个catch',e)
})
// catch捕捉 ReferenceError: action is not defined
// at 000.html:21:10
// at new Promise (<anonymous>)
// at 000.html:20:1
// catch后面的res undefined
- 如果promise的错误一直没处理,那么直到处理后才执行,处理前的所有then都不去执行
new Promise((resolve,reject)=>{
resolve(action)
}).then((res)=>{
console.log('then1.1',res)
}).then((res)=>{
console.log('catch后面的res',res)
}).catch(e=>{
console.log('第二个catch',e)
}).then(res=>{
console.log('then',res)
})
// 第二个catch ReferenceError: action is not defined
// at 000.html:21:10
// at new Promise (<anonymous>)
// at 000.html:20:1
//then undefined
- 中间的then,catch也可以返回新的promise;注意return返回
new Promise((resolve,reject)=>{
resolve('action')
}).then(res=>{
new Promise((reslove,reject)=>{
setTimeout(()=>{
reslove(10)
},1000)
})
}).then(res=>{
console.log('res',res)
})
//res undefined
new Promise((resolve,reject)=>{
resolve('action')
}).then(res=>{
return new Promise((reslove,reject)=>{
setTimeout(()=>{
reslove(10)
},1000)
})
}).then(res=>{
console.log('res',res)
})
//res 10
new Promise((resolve,reject)=>{
resolve('action')
}).then(res=>{
return new Promise((reslove,reject)=>{
setTimeout(()=>{
reject(10)
},1000)
})
}).then(res=>{
console.log('res',res)
},(res)=>{
console.log('error',res)
})
//error 10
- 进入链式then中返回默认是promise
resolved的状态,除非触发Promise.reject操作
let a=new Promise((resolve,reject)=>{
})
console.log(a)
let b=new Promise((resolve,reject)=>{
resolve(1)
})
console.log(b)
let c =new Promise((resolve,reject)=>{
resolve(1)
}).then(res=>{
return 10
})
console.log(c)
let d =new Promise((resolve,reject)=>{
resolve(1)
}).then(res=>{
return new Promise((resolve,reject)=>{
reject(10)
})
}).catch(()=>{
})
console.log(d)
- then处理return处理的值如果是promise会被接管,等待返回的状态后进行下一步
new Promise((resolve,reject)=>{
resolve(10)
}).then(res=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1)
},4000)
})
}).then(res=>{
console.log(res,'res')
console.log(2020)
})
//四秒后执行
//1 'res'
//2020
- then处理return是定时器等异步,会自动执行下一步,然后再处理,下一个then接受的res?
new Promise((resolve,reject)=>{
resolve(20)
}).then(res=>{
return setTimeout(()=>{
console.log('settimeout')
return 1
},5000)
}).then(res=>{
console.log(res,'res')
console.log(2022)
})
// 立刻执行
//2 res //不同的浏览器打印不一样,不一定是2
//2022
// 5秒后执行
// settimeout
- then的穿透能力,一个空的then可以把上一个then的返回值传递给下一个
总结:
- 如果then中的第二个函数已经处理了错误,那么其后的catch代码就不会再去执行
- then中返回值需要加return,
否则下一个then接受到的undefined - catch不会阻止其后的代码继续执行
- resolve,reject和手动error都可以走catch[如:resolve传递一个不存在的参数,报错了]
- then return处理的new Promise会等待结果后再去执行一下个then,如果新promise执行到resolve或者reject,下一个then会去处理返回的结果,如果新promise自己有then,那么最初的promise的下一个then的res为undefined
- then return如果处理的是异步如settimeout不会阻止下个then执行,到最后会返回来执行settimeout
← 类的继承 iterator迭代器 →