♠ 异步处理方案
♣ 异步处理方案
需求:
- 我们需要向服务器发送网络请求获取数据,一共需要发送三次请求;
- 第二次的请求url依赖于第一次的结果;
- 第三次的请求url依赖于第二次的结果;
依次类推;
多次回调、Promise中then
点击查看代码
function requestData(url) {
// 异步请求的代码会被放入到executor中
return new Promise((resolve, reject) => {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
resolve(url)
}, 2000);
})
}
// 需求:
// 1> url: url -> res: url
// 2> url: res + "aaa" -> res: urlaaa
// 3> url: res + "bbb" => res: urlaaabbb
// 1.第一种方案: 多次回调
// 回调地狱
// requestData("url").then(res => {
// requestData(res + "aaa").then(res => {
// requestData(res + "bbb").then(res => {
// console.log(res)
// })
// })
// })
// 2.第二种方案: Promise中then的返回值来解决
//requestData("url").then(res => {
// return requestData(res + "aaa")
// }).then(res => {
// return requestData(res + "bbb")
// }).then(res => {
// console.log(res)
// })
Promise + generator
但是上面的代码其实看起来也是阅读性比较差的,有没有办法可以继续来对上面的代码进行优化呢?第三种方案: Promise + generator实现
点击查看代码
function requestData(url) {
// 异步请求的代码会被放入到executor中
return new Promise((resolve, reject) => {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
resolve(url)
}, 2000);
})
}
//但是上面的代码其实看起来也是阅读性比较差的,有没有办法可以继续来对上面的代码进行优化呢?
// 3.第三种方案: Promise + generator实现
function* getData() {
const res1 = yield requestData("url")
const res2 = yield requestData(res1 + "aaa")
const res3 = yield requestData(res2 + "bbb")
const res4 = yield requestData(res3 + "ccc")
console.log(res4)
}
// 1> 手动执行生成器函数
const generator = getData()
generator.next().value.then(res => {
generator.next(res).value.then(res => {
generator.next(res).value.then(res => {
generator.next(res)
})
})
})
点击查看代码
function requestData(url) {
// 异步请求的代码会被放入到executor中
return new Promise((resolve, reject) => {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
resolve(url)
}, 1000);
})
}
function* getData() {
const res1 = yield requestData("url")
const res2 = yield requestData(res1 + "aaa")
const res3 = yield requestData(res2 + "bbb")
const res4 = yield requestData(res3 + "ccc")
console.log(res4)
}
// 2> 自己封装了一个自动执行的函数
function execGenerator(genFn) {
//拿到生成器
const generator = genFn()
function exec(res) {
const result = generator.next(res)
if (result.done) {
return result.value
}
result.value.then(res => {
exec(res)
})
}
exec()
}
execGenerator(getData)
我们还可以使用TJ:第三方包co自动执行,const co = require('co');co(getData)
异步函数 async function
本质上是Promise + generator的语法糖点击查看代码
function requestData(url) {
// 异步请求的代码会被放入到executor中
return new Promise((resolve, reject) => {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
resolve(url)
}, 2000);
})
}
// 4.第四种方案: async/await
//*—> async yield->await
async function getData() {
const res1 = await requestData("url")
const res2 = await requestData(res1 + "aaa")
const res3 = await requestData(res2 + "bbb")
const res4 = await requestData(res3 + "ccc")
console.log(res4)
}
getData()
async关键字用于声明一个异步函数, async是asynchronous单词的缩写,异步、非同步;sync是synchronous单词的缩写,同步、同时;
点击查看代码
async function foo1() {
}
const foo2 = async () => {
}
class Foo {
async bar() {
}
}
异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行。
异步函数有返回值时,和普通函数会有区别:
- 情况一:异步函数也可以有返回值,但是异步函数的返回值会被包裹到Promise.resolve中;
- 情况二:如果我们的异步函数的返回值是Promise,Promise.resolve的状态会由Promise决定;
- 情况三:如果我们的异步函数的返回值是一个对象并且实现了thenable,那么会由对象的then方法来决定;
点击查看代码
async function foo() {
console.log("foo function start~")
console.log("中间代码~")
console.log("foo function end~")
//默认return undefined;
// 1.返回一个值
// 2.返回thenable
// return {
// then: function(resolve, reject) {
// resolve("hahahah")
// }
// }
// 3.返回Promise
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("hehehehe")
}, 2000)
})
}
// 异步函数的返回值一定是一个Promise
const promise = foo()
//当内部return 会执行这个then ,这个then被执行会加入到微任务队列中
promise.then(res => {
console.log("promise then function exec:", res)
})
如果我们在async中抛出了异常,那么程序它并不会像普通函数一样报错,而是会作为Promise的reject来传递;
点击查看代码
async function foo() {
console.log("foo function start~")
console.log("中间代码~")
// 异步函数中的异常, 会被作为异步函数返回的Promise的reject值的
throw new Error("error message")
console.log("foo function end~")
}
// 异步函数的返回值一定是一个Promise
foo().catch(err => {
console.log("err:", err)
})
console.log("后续还有代码~~~~~")
async函数另外一个特殊之处就是可以在它内部使用await关键字,而普通函数中是不可以的。
await关键字有什么特点呢?
- 通常使用await是后面会跟上一个表达式,这个表达式会返回一个Promise;
- 那么await会等到Promise的状态变成fulfilled状态,之后继续执行异步函数;
- 如果await后面是一个普通的值,那么会直接返回这个值;
- 如果await后面是一个thenable的对象,那么会根据对象的then方法调用来决定后续的值;
- 如果await后面的表达式,返回的Promise是reject的状态,那么会将这个reject结果直接作为函数的Promise的reject值;
点击查看代码
// 1.await跟上表达式
function requestData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve(222)
reject(1111)
}, 2000);
})
}
// async function foo() {
// const res1 = await requestData()
// console.log("后面的代码1", res1)
// console.log("后面的代码2")
// console.log("后面的代码3")
// const res2 = await requestData()
// console.log("res2后面的代码", res2)
// }
// 2.跟上其他的值
// async function foo() {
// // const res1 = await 123
// // const res1 = await {
// // then: function(resolve, reject) {
// // resolve("abc")
// // }
// // }
// const res1 = await new Promise((resolve) => {
// resolve("why")
// })
// console.log("res1:", res1)
// }
// 3.reject值 async function foo(){}是个promise,所以foo().catch
async function foo() {
const res1 = await requestData()
console.log("res1:", res1)
}
foo().catch(err => {
console.log("err:", err)
})