异步编程
2024/7/8大约 3 分钟约 879 字
异步编程
回调函数
1.定时器
setTimeout(callback, time)
let intervalId = setInterval(callback, time)
clearInterval(intervalId)2.事件
键盘按键、鼠标移动、鼠标点击、触摸屏幕动作时,浏览器会生成事件。需要处理特定事件时,可以通过addEventListener注册事件处理。
3.网络事件
网络请求:使用XMLHttpRequest类及回调函数来发送HTTP请求并异步处理服务器返回的响应。
4.Node中的回调和事件
fs.readFile()
Promise
基于回调的异步编程问题:
- 回调多层嵌套的情形,造成代码缩进过多以致难以阅读。
- 难以处理错误。
Promise状态:待定(pending),兑现(fulfill),拒绝(reject)。Promise一旦兑现或被拒绝,我们说它已经落定(settle),永远不会再从兑现变成拒绝,反之亦然。Promise还表示该异步代码的结果:
- 异步代码正常结束,结果为代码的返回值
- 异步代码未正常结束,结果就是一个
Error对象或者某个其他值
对于回调的异步编程,Promise的解决方案:
Promise链,以线性then()方法调用链的形式表达一连串异步操作,每个then()方法返回一个新的promise实例对象.catch()方法,处理错误,在Promise链的任何地方都可以使用。在非末尾使用一般是用来恢复Promise链,继续执行后续代码。
[!tip]
如何理解一个promise与另一个promise发生了关联(或“锁定”了另一个期约)?
当promise以一个promise值解决时,就称之为一个promise与另一个promise发生了关联。
const c1 = response => {
const p4 = response.json()
return p4
}
const c2 = res => {
// ...
}
const p1 = fetch('/api') // 任务1
const p2 = p1.then(c1) // 任务2
const p3 = p2.then(c2) // 任务3
// c1被调用时任务2开始,因为任务2是异步的,任务2在c1回调返回时不会结束。
// c1有三种方式可以终止:
// 第一,它可以正常返回由.json()调用返回的期约。这会导致p2被解决,但该promise是兑现还是被拒绝则取决于新返回的promise。
// 第二,c1可以正常返回null值,这会导致p2立即兑现。
// 最后,c1可以抛出一个错误,这会 致p2被拒绝。并行promise
| 静态方法 | 参数 | 返回值 |
|---|---|---|
| Promise.all() | Promise[] | 如果输入promise中的任意一个拒绝,返回的期约也将拒绝,并带有第一个被拒绝的原因。否则,返回的期约会以每个输入期约兑现值的数组兑现 |
| Promise.allSettled() | Promise[] | 等所有输入promise全部落定后兑现。返回一个对象数组,每个对象都有一个status属性,值为fulfilled或rejected;fulfilled => value; rejected => reason |
| Promise.race() | Promise[] | 返回第一个落定的promise,状态根据返回的promise状态而定,即不关系最终是否成功失败 |
| Promise.any() | Promise[] | 返回第一个兑现的promise,参数是[]或者promise都失败时会抛AggregateError错误 |
// 并行抓取demo,promise长链
function fetchSequentially(urls) {
const bodies = []
function fetchOne(url) {
return fetch(url)
.then(response => response.text())
.then(body => bodies.push(body))
}
let p = Promise.resolve(undefined)
for (const url of urls) {
p = p.then(() => fetchOne(url))
}
return p.then(() => bodies)
}
// 使用
fetchSequentially(url)
.then(res => {
// ...
})
.catch(err => {
// ...
})// 通过then递归调用
function promiseSequence(inputs, promiseMaker) {
inputs = [...inputs];
function handleNextInput(outputs) {
if (inputs.length === 0) {
return outputs
} else {
let nextInput = inputs.shift()
return promiseMaker(nextInput)
.then(output => outputs.concat(output))
.then(handleNextInput)
}
}
return Promise.resolve([]).then(handleNextInput)
}
// 使用
function fetchBody(url) {
return fetch(url).then(r => r.text())
}
promiseSequence(urls, fetchBody)
.then(bodies => {
//
})
.catch(err => {
//
})