一、异步处理
1.1 处理一 - 回调地狱
- 需求:
- 发送第一次网络请求
- 发送第二次网络请求, 依赖第一次请求的结果
- 发送第三次网络请求, 依赖第二次请求的结果
// 封装请求的方法: url -> promise(result)
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
}, 2000)
})
}
// 方式一: 层层嵌套(回调地狱 callback hell)
function getData() {
// 1.第一次请求
requestData("aaa").then(res1 => {
console.log("第一次结果:", res1)
// 2.第二次请求
requestData(res1 + "bbb").then(res2 => {
console.log("第二次结果:", res2)
// 3.第三次请求
requestData(res2 + "ccc").then(res3 => {
console.log("第三次结果:", res3)
})
})
})
}
1.2 处理二 - Promise链式
function getData() {
requestData("aaa").then(res1 => {
console.log("第一次结果:", res1)
return requestData(res1 + "bbb")
}).then(res2 => {
console.log("第二次结果:", res2)
return requestData(res2 + "ccc")
}).then(res3 => {
console.log("第三次结果:", res3)
})
}
1.3 处理三 - generator+yield
function* getData() {
const res1 = yield requestData("aaa")
console.log("res1:", res1)
const res2 = yield requestData(res1 + "bbb")
console.log("res2:", res2)
const res3 = yield requestData(res2 + "ccc")
console.log("res3:", res3)
}
const generator = getData()
generator.next().value.then(res1 => {
generator.next(res1).value.then(res2 => {
generator.next(res2).value.then(res3 => {
generator.next(res3)
})
})
})
1.4 处理四 - await/async
async function getData() {
const res1 = await requestData("aaa")
console.log("res1:", res1)
const res2 = await requestData(res1 + "bbb")
console.log("res2:", res2)
const res3 = await requestData(res2 + "ccc")
console.log("res3:", res3)
}
1.5 封装自动执行生成器函数工具
// 封装请求的方法: url -> promise(result)
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
}, 2000)
})
}
// 生成器的处理方案
function* getData() {
const res1 = yield requestData("aaa")
console.log("res1:", res1)
const res2 = yield requestData(res1 + "bbb")
console.log("res2:", res2)
const res3 = yield requestData(res2 + "ccc")
console.log("res3:", res3)
const res4 = yield requestData(res3 + "ddd")
console.log("res4:", res4)
const res5 = yield requestData(res4 + "eee")
console.log("res5:", res5)
}
// const generator = getData()
// generator.next().value.then(res1 => {
// generator.next(res1).value.then(res2 => {
// generator.next(res2).value.then(res3 => {
// generator.next(res3).value.then(res4 => {
// generator.next(res4)
// })
// })
// })
// })
// 自动化执行生成器函数
function execGenFn(genFn) {
// 1.获取对应函数的generator
const generator = genFn()
// 2.定义一个递归函数
function exec(res) {
// result -> { done: true/false, value: 值/undefined }
const result = generator.next(res)
if (result.done) return
result.value.then(res => {
exec(res)
})
}
// 3.执行递归函数
exec()
}
execGenFn(getData)
二、async/await
2.1 异步函数async
-
async关键字用于声明一个异步函数:- async是asynchronous单词的缩写,异步、非同步
- sync是synchronous单词的缩写,同步、同时
// 普通函数 // function foo() {} // const bar = function() {} // const baz = () => {} // 生成器函数 // function* foo() {} // 异步函数 async function foo() { console.log("foo function1") console.log("foo function2") console.log("foo function3") } foo() // const bar = async function() {} // const baz = async () => {} // class Person { // async running() {} // }
2.2 异步函数返回值
在 JavaScript 中,
async函数总是返回一个Promise对象。即使函数内部返回的是一个数组或其他值,它最终会被包裹在一个Promise对象中。
-
异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行
-
异步函数有返回值时,和普通函数会有区别:
- 如果异步函数的返回值是普通值,相当于被包裹到Promise.resolve中
- 如果异步函数的返回值是Promise,状态由会由Promise决定
- 如果异步函数的返回值是一个对象并且实现了thenable,那么会由对象的then方法来决定
async function foo2() { // 1.返回一个普通的值 // -> Promise.resolve(321) return ["abc", "cba", "nba"] // 2.返回一个Promise // return new Promise((resolve, reject) => { // setTimeout(() => { // resolve("aaa") // }, 3000) // }) // 3.返回一个thenable对象 // return { // then: function(resolve, reject) { // resolve("bbb") // } // } } foo2().then(res => { console.log("res:", res) }) -
注意:如果在async中抛出了异常,程序并不会像普通函数一样报错,而是会作为Promise的reject来传递
// 如果异步函数中有抛出异常(产生了错误), 这个异常不会立即被浏览器处理 // 进行如下处理: Promise.reject(error) async function foo() { console.log("---------1") console.log("---------2") // "abc".filter() throw new Error("async function error") console.log("---------3") // return new Promise((resolve, reject) => { // reject("err rejected") // }) return 123 } // promise -> pending -> fulfilled/rejected foo().then(res => { console.log("res:", res) }).catch(err => { console.log("err:", err) console.log("继续执行其他的逻辑代码") })
2.3 async中使用await
-
async函数另外一个特殊之处就是可以在它内部使用await关键字,而普通函数中是不可以的
-
await关键字的特点- 通常使用await是后面会跟上一个表达式,这个表达式会返回一个Promise,那么await会等到Promise的状态变成fulfilled状态,之后继续执行异步函数;
function bar() { console.log("bar function") return new Promise(resolve => { setTimeout(() => { resolve(123) }, 100000) }) } async function foo() { console.log("-------") // await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码 const res1 = await bar() console.log("await后面的代码:", res1) const res2 = await bar() console.log("await后面的代码:", res2) console.log("+++++++") } foo() -
await后面跟不同的值的情况- 如果await后面是一个普通的值,那么会直接返回这个值;
- 如果await后面是一个thenable的对象,那么会根据对象的then方法调用来决定后续的值;
- 如果await是一个promise对象,会阻断后续的代码,等待状态变为
fullfilled,才获取结果并执行后续代码;如果返回的promise是reject状态,会将这个reject结果直接作为Promise的reject值
(async function () { const p = Promise.reject("err"); // rejected状态 try { const res = await p; } catch (e) { console.log(e); // err // try...catch 相当于 Promise 的 catch } })(); (async function () { const p = Promise.reject("err"); // rejected状态 const res = await p; // await 相当于promise的then console.log("res", res); // 不会执行 })(); -
await后不仅可以跟一个普通函数返回一个Promise,也可以跟一个异步函数// 1.定义一些其他的异步函数 function requestData(url) { console.log("request data") return new Promise((resolve) => { setTimeout(() => { resolve(url) }, 3000) }) } async function test() { console.log("test function") return "test" } async function bar() { console.log("bar function") return new Promise((resolve) => { setTimeout(() => { resolve("bar") }, 2000); }) } async function demo() { console.log("demo function") return { then: function(resolve) { resolve("demo") } } } // 2.调用的入口async函数 async function foo() { console.log("foo function") const res1 = await requestData("abc") console.log("res1:", res1) const res2 = await test() console.log("res2:", res2) const res3 = await bar() console.log("res3:", res3) const res4 = await demo() console.log("res4:", res4) } foo()