我们在平时开发的过程中经常会遇到一个棘手的问题,就是大环境下是同步调用的方式,但是有一个外部库使用了链式,所以不得不对整个调用栈都进行链式重写,这就被称之为异步调用的传染性。今天来对消除async和await的传染性做一些探讨
首先来看一个例子
'use strict'; async function fetchData() { return await fetch('https://bigonion.cn') .then(response => response.text()) } async function main() { let _data _data = await fetchData() console.log(_data.toString().substring(0, 200)) } main()
在这个案例中,我们用fetch来请求网站拿到源码,由于fetch是异步调用,在所有调用了fetchData的函数必须要带上async或者以promise的方式被使用。
那么如果我想让fetchData不带上async和await并能够让_data
顺利拿到请求的结果怎么办?
直接去掉async await吗?
function fetchData() { return fetch('https://bigonion.cn') .then(response => response.text()) } function main() { let _data _data = fetchData() console.log(_data) } main()
显然这是不行的,因为这样_data
拿到的就是一个promise类型,打印出来的结果也是一个promise对象
这里问题出在fetch是异步上,所以我们需要重新对fetch进行设计,由于涉及到修改fetch,所以我们得在一个闭包里面运行,此闭包里面的fetch是会被修改的,也就是提供一个新的fetch函数。
当然这里实现的fetch比较简陋,不具备then属性,所以调用的时候把处理数据放在changedFetchEnv
函数中
function fetchData() { return fetch('https://bigonion.cn') } function main() { let _data _data = fetchData() console.log("页面GET到的内容\n:", _data.toString().substring(0, 200)) } function changedFetchEnv(func){ func() } changedFetchEnv(main())
下面对fetch进行重写:
_fetch
是原fetch
函数
result
结构,具有status、data、err
属性
cache
缓存里fetch
函数_fetch
来请求result.status
为 "fulfilled" 状态result.status
为 "rejected" 状态result.data
为 请求到的结果throw
方法抛出本次请求的_promise
对象,来立刻终止异步进程
i
。
function fetchData() { return fetch('https://bigonion.cn') } function main() { let _data _data = fetchData() console.log("页面GET到的内容\n:", _data.toString().substring(0, 200)) } function changedFetchEnv(func) { let cache = [] let i = 0 let _fetch = window.fetch window.fetch = (...args) => { if (cache[i]) { if (cache[i].status === "fulfilled") { return cache[i].data } else if (cache[i].status === "rejected") { throw cache[i].err } } const result = { status: "pending", data: null, err: null } cache[i++] = result const _promise = _fetch(...args) .then(res => res.text()) .then( (res) => { result.status = "fulfilled" result.data = res }, (err) => { result.status = "rejected" result.err = err }) throw _promise } try { func() } catch (err) { if (err instanceof Promise) { i = 0 err.then(func, func); } } } changedFetchEnv(main)
最后我们发现,即使不用async和await关键字,通过异常抛出的方式,我们一样能把异步的函数变成同步的方式去写代码,并且能甩掉async和await关键字,防止了污染调用栈。
对于这种异常抛出的方法,我个人认为还是有非常大的可能性的,的的确确把看起来完全不可能消除的副作用给湮灭了,而且还有很大的可复用性。听说这个方法在React中已经被广泛使用了,具体情况可以看下面视频链接,还有实现的思维导图讲解,说的还是比较详细的。
也希望大家能给出一些建议和意见,对于这种方法,你怎么看?
作者:bigonion
邮箱:bigonion@bigonion.cn
NameSpace: 大聪花的家
Origin: 大聪花的博客
Powered by markdown 在线
声明:未经本人同意,禁止转载、搬运、抄袭!
"2023.12.13"