回调函数
ES5:使用回调函数处理异步执行的结果
setTimeout(() => {
console.log('1')
setTimeout(() => {
console.log('2')
setTimeout(() => {
console.log('3')
//....
}, 1000)
}, 1000)
}, 1000)
多重回调嵌套导致可读性变差,称为回调地狱(Callback Hell)
良好的编程习惯:http://callbackhell.com/
Promise
ES6语法
承诺,承诺在未来的某个时刻返回数据。
优点:使用链式结构将多个异步操作串联起来,解决了回调地狱问题
new Promise(...)
.then(...)
.then(...)
.then(...)
.catch(...)
.finally(...)
Promise.then() 和 Promise.catch() 都返回 Promise 对象,示例:
new Promise(function (resolve, reject) {
setTimeout(() => {
var start = 3
resolve(start)
}, 1000)
})
// 异常之后、catch之前的部分不被执行。类似 try-catch中的异常捕获
.then(function (res) {
console.log('1st then, ', res)
throw Error('e')
res *= 2
return res
})
.then(function (res) {
console.log('2nd then, ', res)
return res * 2
})
.then((res) => {
console.log('3rd then, ', res)
})
// 异常捕获
.catch((err) => {
console.log('err, ', err)
})
// 异常之后的then可执行
.then(() => {
console.log('after catch')
})
// 清理、结尾工作,比如关闭进度条
.finally(() => {
console.log('final')
})
基于Promise的异步请求
<script>
'use strict'
function reqPromise(URL) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('GET', URL)
xhr.onload = function () {
if (xhr.status == 200) {
resolve(xhr.response)
} else {
reject(Error('error code: ', xhr.statusText))
}
}
xhr.onerror = function () {
reject(Error('network error.'))
}
xhr.send()
})
}
reqPromise('https://jsonplaceholder.typicode.com/posts/1').then(
(resp) => {
console.log('yay!, ', JSON.parse(resp))
},
(Error) => {
console.log(Error)
}
)
</script>
基于fetch-api(XMLHttpRequest的升级版) 的异步请求
fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'get',
}) // fetch 返回 Promise对象
.then((resp) => {
return resp.json() // Body.json() 返回 Promise 对象
})
.then((data) => {
console.log(data)
})
async/await
基于 Promise 之上的语法糖,使异步操作简单、明了。
注意:
-
使用 async 将函数标记为异步函数(返回 Promise 对象的函数)
-
在异步函数中调用其他的异步函数,不再使用 then,而是使用await,await 会等待 Promise 执行完成返回最终的结果(await 底层基于 Promise 和事件循环机制实现,在等待时会处理其他任务,比如页面更新等等)
async function fun() {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1')
const data = await resp.json()
console.log(data)
}
fun();
await 陷阱 ✧
1、并行操作
async function f() {
const promiseA = await fetch('http://...')
const promiseB = await fetch('http://...') //两个 fetch 不是并行的
//...
}
// 更高效的做法:将Promse用Promise.all组合起来,然后再去await,修改后的程序运行效率会直接提升一倍
async function f() {
const promiseA = fetch('http://...')
const promiseB = fetch('http://...')
const [a, b] = await Promise.all([promiseA, promiseB])
}
不使用Promise.all时:
使用Promise.all后:
2、再循环中执行异步操作,不能调用 forEach或map 这样的方法
async function f1() {
[1, 3, 5].forEach(async (i) => { //⚠️:虽然使用了await,但forEach会立刻返回,并不会暂停等到所有异步操作都执行完毕!
await doSomethingOperation();
})
consolo.log('done')
}
f1();
//解决方法一:使用传统的for循环 =》 会暂停等到所有异步操作都执行完毕
async function f2() {
for (let i of [1, 2, 3]) {
await doSomething();
}
consolo.log('done')
}
//解决方法二:使循环中的操作并发执行,使用 for await:
async function f3() {
const promises = [
someAsyncOp();
someAsyncOp();
someAsyncOp();
]
for await (let result of promises) {
// ....
}
console.log('done')
}
3、不能在全局中或普通函数中使用 await,而只能被用在异步函数中
async funcion f() {
await doSomething();
}
f();
// 更简洁
(async ()=> {
doSomething()
})();
有了 async/await,几乎用不到 Promise 及他的 then、catch 方法
其他:引入并使用 async 模块
Reference
https://www.bilibili.com/video/BV1WP4y187Tu
标签:异步,console,log,await,JS,Promise,async From: https://www.cnblogs.com/engure/p/17135865.html