-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
第 12 题:JS 异步解决方案的发展历程以及优缺点。 #11
Comments
JS 异步已经告一段落了,这里来一波小总结 1. 回调函数(callback)setTimeout(() => {
// callback 函数体
}, 1000) 缺点:回调地狱,不能用 try catch 捕获错误,不能 return 回调地狱的根本问题在于:
ajax('XXX1', () => {
// callback 函数体
ajax('XXX2', () => {
// callback 函数体
ajax('XXX3', () => {
// callback 函数体
})
})
}) 优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。) 2. PromisePromise就是为了解决callback的问题而产生的。 Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新 Promise,如果我们在 then 中 return ,return 的结果会被 Promise.resolve() 包装 优点:解决了回调地狱的问题 ajax('XXX1')
.then(res => {
// 操作逻辑
return ajax('XXX2')
}).then(res => {
// 操作逻辑
return ajax('XXX3')
}).then(res => {
// 操作逻辑
}) 缺点:无法取消 Promise ,错误需要通过回调函数来捕获 3. Generator特点:可以控制函数的执行,可以配合 co 函数库使用 function *fetch() {
yield ajax('XXX1', () => {})
yield ajax('XXX2', () => {})
yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next() 4. Async/awaitasync、await 是异步的终极解决方案 优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题 缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。 async function test() {
// 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
// 如果有依赖性的话,其实就是解决回调地狱的例子了
await fetch('XXX1')
await fetch('XXX2')
await fetch('XXX3')
} 下面来看一个使用
对于以上代码你可能会有疑惑,让我来解释下原因
上述解释中提到了 本文首发于我的博客:JS异步解决方案的发展历程以及优缺点 |
@sisterAn 没有依赖关系的异步操作不使用 async function test() {
// 以下代码没有依赖性的话,不使用 await 便不会阻塞运行
fetch('XXX1')
fetch('XXX2')
fetch('XXX3')
} |
@sisterAn 规范里好像没提到 await 内部实现了 generator, 如果从 polyfill 中的实现去断定 await 内部就是 generator……这样好像有点不严谨…… 另外 var a = 0
var b = () => {
var temp = a;
Promise.resolve(10)
.then((r) => {
a = temp + r;
})
.then(() => {
console.log('2', a)
})
}
b()
a++
console.log('1', a) |
请教一下,为何 b 里不是直接访问 a? |
@duanzheng |
其实提到了 Promise.al 就说明这里有个隐含前提,尽管三个 fetch 之前没有依赖,但是需要等待三个 fetch 都已经完成了再执行下一句。 |
|
需要好好理解的是async, await VS Generator改进了什么
|
Promise 如果在这样写 |
我尝试以下代码片段,按照您这样模拟是行不通的,不太理解为什么我这样写async里面输出就是 11: let a = 0
let b = async () => {
let c = await 10
a = a + c
console.log('2', a) // 2 11
}
b()
a++
console.log('1', a) // 1 1 |
|
上面说了执行到 await 的时候会保留 堆栈中的东西,这个时候变量a并没有使用,所以并没有保留 a = 0;当 await 结束后,再使用变量a,此时a的值经过 a++ 已经变成了 1 了。所以最后输出的是11。 |
最早的异步的实现应该: |
@sisterAn 其实最后async await代码可以翻译成下面这样就清爽了: let a = 0
let b = async () => {
const c = await 10;
a = a + c;
console.log('2', a) // -> '2' 10
}
b()
a++
console.log('1', a) // -> '1' 1 async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行 |
上一个console,a++之后的,变成1了;下一个console,tmp存的是a++之前的,值为0 |
|
你这样的写法a = a + c已经在一个微任务队列中,这时候由于同步任务a++先执行,导致a被读取时为1,你的写法等价于 let a = 0;
let b = async () => {
let c;
Promise(10).then(r => {
c = r;
})
.then(_ => {
a = a + c; // 此时a++,已经在第一次宏任务中执行过
console.log('2', a) // '2' ,11
});
}
b()
a++ |
promise的缺点有三个: |
还是会有点影响,这样才不会阻塞
} |
我试了一下打印出的还是2,11啊 |
@inJs 虽然这三个异步操作没有依赖关系,那要是函数内下面的某个操作需要他们三个全部返回才能处理怎么办呢?所以还是得用await,否则连async函数都没必要写。 async function test(){
const cbs = await Promise.all([fetch('XXX1'), fetch('XXX2'),fetch('XXX3')])
// ...其他需要使用cbs的操作
} |
@wfymalong 如果把异步函数提前赋值给变量,就不需要用Promise.all了吧,人家给的例子是正确的。 async ƒ test() {
// 这样不会阻塞性能把
let a = fetch('XXX1') // 我认为赋值的同时已经在执行异步函数了
let b = fetch('XXX2') // 这样就是并发执行三个异步函数
let c = fetch('XXX3') // 就不存在阻塞了
let aa = await a
let bb = await b
let cc = await c
console.log(aa,bb,cc)
} |
没人提rxjs吗。 |
AsyncFunctionBody 中遇到 await 关键字的时候,会解析成 AwaitExpression 并等待其返回结果。上面代码中,对表达式 相关流程可以感受下: ;(async () => {
console.log("sync 0")
var a =
(console.log("sync 1"), await "", console.log("async 3"), await "resolved")
console.log("a: ", a)
})()
console.log("sync 2")
// 打印顺序:
// sync 0
// sync 1
// sync 2
// async 3
// a: resolved 另外,书写时推荐 await 放在表达式的开头。ESLint 规则:https://cn.eslint.org/docs/rules/require-atomic-updates |
Rxjs |
回调 -》 回调地域 |
有个疑问: 不能说回调函数是异步的解决方案吧, 不是用在setTimeout等这些异步方法内的回调函数才算得上是异步吗 |
对,准确来说,应该叫 “异步回调” ,不过 回调函数经常被用于在一个异步操作完成后执行代码,这里这么说也合理 |
参考:阮一峰异步编程
The text was updated successfully, but these errors were encountered: