异步
异步调用时,调用方不等被调方返回结果就转身离去,因此必须有一种机制让被调方有了结果时能通知调用方。在同一进程中有很多手段可以利用,常用的手段是回调、event 对象和消息
必要时甚至可以把异步函数转换为同步函数。方法很简单:调用异步函数后马上调用 wait 函数等在那里,待异步函数返回结果后再继续往下走。
多线程
有了多线程的支持,可以采用异步调用,调用方和被调方可以属于两个不同的线程,调用方启动被调方线程后,不等对方返回结果就继续执行后续代码。被调方执行完毕后,通过某种手段通知调用方:结果已经出来,请酌情处理
参考:https://www.cnblogs.com/balingybj/p/4780442.html
回调函数实现异步:
document.onclick = function () {
console.log('此处异步');
};
document.addEventListener('click',
() => {
console.log('此处异步');
}, false);
console.log('此处同步');
解决回调地狱问题
回调地狱
callback hell
层层嵌套的函数,是纵向若业务需求改变不易修改函数的顺序
<style>
* {
margin: 0;
padding: 0;
}
#box {
width: 100px;
height: 100px;
background-color: blue;
transition: all .5s;
}
</style>
<body>
<div id="box" class="box"></div>
<script>
// 三个参数:
// obj:运动的元素
// json对象格式:元素的属性和值
// endFn: 回调函数(可省)
const move = (el, { x = 0, y = 0 } = {},
end = () => { }) => {
el.style.transform = `translate3d(${x}px,${y}px,0)`;
el.addEventListener(
'transitionend',
() => {
console.log('end');
end();
},
false
);
};
const boxEl = document.getElementById('box');
document.addEventListener('click',
() => {
move(boxEl, { x: 100 }, () => {
move(boxEl, { x: 100, y: 100 }, () => {
move(boxEl, { y: 100 }, () => {
move(boxEl, {}, () => {
});
});
});
});
},
false);
</script>
</body>
用法
const p = new Promise((resolve,reject) => { });
其中resolve必需,reject可选
状态
有3种
1)完成实例化,默认状态(初始化状态)为pending
2)resolve()执行,状态变为fulfilled/resolved
3)reject()执行,状态变为rejected,并报错
状态只变一次
const p = new Promise((resolve, reject) => {
reject();
resolve();
});
console.log(p);//Promise {<rejected>: undefined}
const p = new Promise((resolve, reject) => {
resolve();
reject();
});
console.log(p);//Promise {<fulfilled>: undefined}
实例方法
then()
1.根据状态变化,执行两个回调
具体是:
pending->fulfilled,执行第一个回调函数
pending->rejected,执行第二个回调函数,并报错
2.返回Promise对象
const p = new Promise((resolve, reject) => {
// reject();
resolve();
});
const p2 = p
.then(
() => { },
() => { }
);
console.log(p, p2, p2 === p);
//Promise {<fulfilled>: undefined} Promise {<pending>} false
返回的Promise对象状态可改变
1)默认执行resolve(), 返回成功状态的Promise对象
return undefined;
等价于
return new Promise(resolve => {
resolve(undefined);
});
return 123;
等价于
return new Promise(resolve => {
resolve(123);
});
2)自定义执行reject(), 返回失败状态的Promise对象
return reason;
等价于
return new Promise(resolve => {
reject('reason');
});
3)抛出错误,效果相当
向后传值
const p = new Promise((resolve, reject) => {
// reject();
resolve();
});
p.then(
() => {
console.log('success');
return undefined;
},
() => {
console.log('err');
return undefined;
}
).then(
data => {
console.log('success2', data);
},
() => {
console.log('err2');
}
);
分析:p执行resolve();
1)第一个then()执行第一个回调函数
动作1:打印success;
动作2返回一个成功状态的Promise对象
2)第二个then(),接收了返回值,执行第一个回调函数
3)若想返回一个失败状态的Promise对象,需重新自定义返回语句;第二个then(),接收了返回值,执行第二个回调函数
解决回调地狱
<style>
* {
margin: 0;
padding: 0;
}
#box {
width: 100px;
height: 100px;
background-color: blue;
transition: all .5s;
}
</style>
</head>
<body>
<div id="box" class="box"></div>
<script>
// 运动函数
const move = (el, { x = 0, y = 0 } = {}, end = () => { }) => {
// 模板字符串
el.style.transform = `translate3d(${x}px,${y}px,0)`;
el.addEventListener(
'transitionend',
() => {
console.log('end');
end();
},
false
);
};
// 改造move函数
// point为坐标参数
// reject参数可省
const movePromise = (el, point) => {
return new Promise(resolve => {
move(el, point, () => {
resolve();
});
})
};
const boxEl = document.getElementById('box');
// 运用then()实现纵向嵌套变横向传递
document.addEventListener('click',
() => {
movePromise(boxEl, { x: 100 })
.then(() => {
return movePromise(boxEl, { x: 100, y: 100 });
}).then(() => {
return movePromise(boxEl, { y: 100 });
}).then(() => {
return movePromise(boxEl, {});
});
},
false);
</script>
catch()-状态为rejected时执行
then只写一个回调函数,专门处理resolved状态
then(
data => { },
err => { }
);
then(
data => { }
);
是then()特例
相当于
then(
null,
err => { }
);
用来捕获错误,之后错误消失
一般地,Promise对象后紧跟catch(),先处理Promise内部错误
new Promise((resolve, reject) => {
reject('reason');
})
.catch(err => {
console.log(err);//reason
// 默认返回值
// return undefined;
})
.then(
data => {
console.log(data);//undefined
},
);
连环捕获
多个catch(),前一个catch()有错误
new Promise((resolve, reject) => {
reject('reason');
})
.catch(err => {
console.log(err);//reason
// 与自定义失败状态的Promise对象的效果相当
throw new Error('reason');
})
.catch(err => {
console.log(err);//Error: reason
});
finally()-状态发生变化时就执行
不接收返回值
new Promise((resolve, reject) => {
resolve(123);
// reject('reason');
})
.finally(data => {
console.log(data);
// resolve()reject()均输出undefined
})
.catch(err => { });
应用举例:操作数据库成功失败与否,之后都需关闭数据库
是then()特例
new Promise((resolve, reject) => {
// resolve(123);
reject('reason');
})
// finally等同于
.then(
result => {
return result;
},
err => {
throw err;
// throw等同于
// return new Promise((resolve, reject) => {
// reject(err);
// })
}
)
.then(data => {
console.log(data);
})
.catch(err => {
console.log(err);
});
构造函数方法
Promise.resolve()-成功状态Promise的简写
new Promise(resolve => resolve('foo'));
简写为
Promise.resolve('foo');
返回值:Promise原对象,不做其他
例:
1.参数-一般参数
Promise.resolve(‘foo’)
.then(data => {
console.log(data);//foo
});
2.参数-Promise对象
const p1 = new Promise(resolve => {
setTimeout(resolve, 1000, '我执行了');
// 相当于
// setTimeout(() => {
// console.log('我执行了');
// }, 1000);
});
Promise.resolve(p1)
.then(data => {
console.log(data);//我执行了(2nd)
});
// 相当于
// p1.then(data => {
// console.log(data);
// });
console.log(Promise.resolve(p1) === p1);//true(1st)
简写还原
resolve()函数接收Promise对象时,then根据传递的Promise状态变化决定执行哪个回调
const p1 = new Promise(resolve => {
setTimeout(resolve, 1000, '我执行了');
});
new Promise(resolve => resolve(p1)).then(data => {
console.log(data);
});
3.参数-具有then()方法的对象
思考:如何实现Promise对象调用then()一样的效果
问题:不执行then()
const thenable = {
then() {
console.log('then');
// 不可用return语句设置返回值
// then()不在Promise对象中
//return语句无法被包装为Promise对象
// return 123;
}
};
// 调用了thenable的then()方法
// 输出then
// 仍处于pending状态,故不执行then()
Promise.resolve(thenable)
.then(
data => console.log(data),
err => console.log(err)
);
解决:
const thenable = {
then(resolve, reject) {
// resolve('then');
reject('reason');
}
};
Promise.resolve(thenable)
.then(
data => console.log(data),
err => console.log(err)
);
Promise.reject ()-失败状态Promise的简写
new Promise(resolve,reject => reject ('foo'));
简写为
Promise. reject ('foo');
参数-原封不动向后传递
1.Promise对象
不执行其中的语句
const p1 = new Promise(resolve => {
setTimeout(resolve, 1000, '我执行了');
});``
Promise.reject(p1)
.catch(err => console.log(err) //Promise {<pending>}
);
2.一般参数
1)简化还原形式
new Promise((resolve, reject) => {
resolve(123);
})
.then(data => {
// 默认返回成功状态Promise
return data;
})
.then(data => {
console.log(data);//123
});
2)传一般参数
new Promise((resolve, reject) => {
resolve(123);
})
.then(data => {
// 自定义返回失败状态Promise
return Promise.reject('reason');
})
.catch(err => {
console.log(err);//reason
});
Promise.all()-取决于所有传入的Promise状态
返回值:新的Promise
所有Promise状态为resolved,其状态才为resolved
存在一个Promise状态为rejected,其状态就为rejected
分析:
1)第1s, p1返回成功对象,第2s,p2返回成功对象,同时p执行第一个回调函数,打印[‘p1’, ‘p2’]
2) 第1s, p1返回成功对象,第2s,p2返回失败对象,同时p执行第二个回调函数,打印’p2’
return Promise.reject(‘p2’);
3)第1s, p1返回失败对象,同时p执行第二个回调函数,打印’p1’ ;第2s,p2返回成功对象
return Promise.reject('p1');
// 延迟函数
const delay = ms => {
return new Promise(
resolve => {
// 执行resolve()
// 返回成功状态的对象1
setTimeout(resolve, ms);
}
);
};
const p1 = delay(1000).then(() => {
console.log('p1完成了');
// 返回成功状态的对象2.1
return 'p1';
});
const p2 = delay(2000).then(() => {
console.log('p2完成了');
// 返回成功状态的对象2.2
return 'p2';
});
// 返回成功状态的对象3
const p = Promise.all([p1, p2]);
p.then(
data => { console.log(data); },
err => { console.log(err); }
);
Promise.race()-取决于第一个传入的Promise状态
Promise.allSettled()-状态恒为成功
与传入的Promise状态无关
记录传入的各Promise状态
注意:参数非Promise,会默认转为成功状态的Promise
Promise.all([1, 2, 3]).then(datas => { console.log(datas); });
// 等价于
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]).then(datas => { console.log(datas); });
错误可单独,也可统一处理
1)单独
const delay = ms => {
return new Promise(
resolve => {
setTimeout(resolve, ms);
}
);
};
const p1 = delay(1000).then(() => {
return 'p1';
return Promise.reject('p1');
}).catch(err => {
console.log('p1', err);
});
const p2 = delay(2000).then(() => {
return 'p2';
return Promise.reject('p2');
}).catch(err => {
console.log('p2', err);
});
const p = Promise.allSettled([p1, p2]);
p.then(
data => { console.log(data); },
err => { console.log(err); }
);
2)统一
const delay = ms => {
return new Promise(
resolve => {
setTimeout(resolve, ms);
}
);
};
const p1 = delay(1000).then(() => {
return 'p1';
return Promise.reject('p1');
});
const p2 = delay(2000).then(() => {
return 'p2';
return Promise.reject('p2');
});
const p = Promise.allSettled([p1, p2]);
p.catch(err => {
console.log(err)
})
.then(
data => { console.log(data); },
err => { console.log(err); }
);
应用-异步加载图片
<img
src="https://img3.mukewang.com/61df86410001bf0517920764.jpg"
alt=""
id="img"
/>
<script>
const loadImgAsync = (url) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve(img);
};
img.onerror = () => {
reject(new Error(`Couldn't load image at ${url}`));
};
img.src = url;
});
};
const imgDOM = document.getElementById("img");
loadImgAsync("https://img2.mukewang.com/61df86770001052f17920764.jpg")
.then((img) => {
// console.log(img);
// console.log(img.src);
imgDOM.src = img.src;
})
.catch((err) => {
console.log(err);
});
</script>