异步编程新新新新手, 不应该说编程新手
这几日在用 jsbox 构建一个自己的小工具,卡在了 http 异步请求这一块,请求返回值死活无法赋值给一个全局的变量给其他地方使用;
了解了异步的一些基础用法,有通过 then 获取的,有通过定义 async 函数获取的,还有回调函数获取的 但发现有个特点就是拿到的返回值,作用域均仍在内部,拿不出来这个返回值
// 调用 v 站的 api 例子,获取最新主题
function test() {
var result = undefined
let url = "https://www.v2ex.com/api/topics/latest.json"
return $http.get({
url: url
}).then(resp => {
var data = resp.data[0]['title']
return data
})
}
//调用的获取返回值
async function getRet(){
var ret = await test();
console.log(ret);
return ret;
}
getRet() // 拿不到 ret 的值
还是交待一下上下文吧,我的原始需求: 我在jsbox中构建一个webview视图
// 视图层
views: [
{
type: "label",
props: {
text: "",
url: test() // 这个位置需要用到http异步请求的返回值
},
events: {
tapped: function() {
$app.close(0);
}
}
}
定义的请求http接口的函数 :
// 逻辑层
function test() {
var result = undefined
let url = "https://www.v2ex.com/api/topics/latest.json"
return $http.get({
url: url
}).then(resp => {
var data = resp.data[0]['title']
return data
})
}
在视图层,我应该如何把test的返回值传递过去?正确的做法是什么?
1
Yumwey 2020-10-24 21:57:34 +08:00 via Android
async/await 返回的是 promise,所以,知道怎么拿了吗?
|
2
kuanng 2020-10-24 22:00:24 +08:00
getRet 这个函数返回的是一个 promise 吧
|
3
Jirajine 2020-10-24 22:12:13 +08:00 via Android
异步就是这样的啊,你要非要用同步的风格写异步,那就把代码逻辑包在一个 async IIFE 里面,像这样
(async function(){ // your code here })() 然后用 await 就能拿到包在 promise 里面的值了。 还有个极其不推荐的办法是定义一个全局变量,然后异步代码里把结果赋值给这个变量,但你无法保证你取用这个变量的代码被调度到后面执行。 |
4
css3 OP @css3 @Yumwey @kuanng
这样可以,但我后面要用这个返回值的地方,是构建一个 webvirw, 它是不支持直接给一坨东西的,还是得赋值给变量才能用啊 https://gist.github.com/seryte/e725987b2b18ccb43772a2b8ea182198 |
5
vision1900 2020-10-24 22:20:27 +08:00
你在写 test 的时候就已经拿到了值,只是这个是异步的
无论你怎么封装,都不可能用同步的方式拿到值 async/await 是一层语法糖,只是写起来像同步的而已,实际还是依赖 promise 。也就是说实际还是异步代码 getRet() 运行的上下文是同步的,一开始方向就搞错了,陷入了“封装陷阱” |
6
css3 OP @vision1900 😂非常的对,我已经陷入封装循环了,还跳不出来异步这个“坎”
|
8
css3 OP @vision1900 所以,我怎么用同步的方法拿到异步的返回值?
|
9
Yumwey 2020-10-24 22:32:55 +08:00 via Android
你非要同步的话,又不想再套 async/await 走逻辑的话,那就写个 emit
|
11
Jirajine 2020-10-24 22:38:55 +08:00 via Android
@css3 你还是没看我在说什么。
把所有代码都包进去,如果用全局变量赋值的话,很可能你后面取用的代码会先于你给全局变量赋值的异步代码执行,所以你会“取不到”,因为你取的时候还没赋值呢。这就是异步。 |
12
vision1900 2020-10-24 22:53:35 +08:00 3
@css3 没有办法,同步代码不可能立即获得异步的返回,只能等待环境(浏览器或者 Node )告诉你异步代码执行结束了,你才能真正“拥有”返回值. 处理返回,要不用回调函数,要不用 Promise
var result = undefined let url = "https://www.v2ex.com/api/topics/latest.json" return $http.get({ url: url }).then(resp => { var data = resp.data[0]['title'] // 这里你获得了返回,直接用就完了 }) 如果你需要封装成函数让代码看起来更模块化,可以传一个回调函数 funtion test(callback) { var result = undefined let url = "https://www.v2ex.com/api/topics/latest.json" return $http.get({ url: url }).then(resp => { var data = resp.data[0]['title'] callback(data); }) } function useData(data) { // 任何依赖于异步返回的代码都放这里 console.log(data) } test(useData); // 高级一点的做法是返回一个 Promise funtion test() { var result = undefined let url = "https://www.v2ex.com/api/topics/latest.json" return $http.get({ url: url }).then(resp => { var data = resp.data[0]['title'] return data; // .then 返回类型是 Promise,这个 Promise 会 resolve 为 .then 里的函数返回值 }) } test().then(data => { // 任何依赖于异步返回的代码都放这里 console.log(data) }) // 再高级一点是用 async/await,不过心里要明白,async/await 只是语法糖,实际还是 promise 。之所以高级是因为视觉上像同步代码,增强了可读性 funtion test() { var result = undefined let url = "https://www.v2ex.com/api/topics/latest.json" return $http.get({ url: url }).then(resp => { var data = resp.data[0]['title'] return data }) } async main() { const data = await test(); // 任何依赖于异步返回的代码都放这里 console.log(data) } main(); |
13
hazyzh 2020-10-24 23:18:57 +08:00 2
我怎么用同步的方法拿到异步的返回值?
??? 这个问题问的非常好!这需要利用量子力学的一个概念 **协变量子场**, [空间和时间都不是连续的,宇宙时间,世界的一切,包括时空,都只是协变量子场的表现形式] 。 建议你研究一下这个,这个问题一解决 可以从根本上一举攻克 js 回掉地狱的历史性难题。 |
14
autoxbc 2020-10-24 23:26:14 +08:00
异步是语言级的特性,没有「不支持这样的格式」的说法
|
15
GzhiYi 2020-10-24 23:57:31 +08:00
关键字是:宏任务和微任务
|
16
aaronlam 2020-10-25 02:07:50 +08:00
你应该要把需要用到这个 await 返回的值的操作直接就写在 await 之后,这是最简单的办法了。因为 async await 说白了其根本就是 promise,你可以理解为 await 之后的代码还原成 promise 之后,就是在 then 里面的代码。
``` javascript new promise((resolve, reject)=>{...}).then((value)=>{ // await 之后的代码就是类似在这里的代码!!] })。 ``` 要是你非要把 then 里的 value 拿出来(但是按照我的理解,貌似这样有点脱裤子放屁的感觉),那按照你目前提供的代码,可以在外部写一个类似下面 getAsyncReturnValue 的函数 ``` javascript function getAsyncReturnValue(value) { // 这里做你要拿到 value 后要做的事 } function test() { var result = undefined let url = "https://www.v2ex.com/api/topics/latest.json" $http.get({ url: url }).then(resp => { var data = resp.data[0]['title'] return data }) } (async ()=> { const value = await test(); getAsyncReturnValue(value) }()) //调用的 ``` |
17
kwrush 2020-10-25 04:20:58 +08:00 1
想要同步获取异步结果,那可以自己实现一个简单的 event emitter 或者观察者模式,比如
class MyEmitter () { constructor() { this._events = []; } on = (event, listener) => { if (this._events[event] == null) { this._events[event] = []; } this._events[event].push(listener); } removeListener = (event, listener) => {...} emit = (event, data) => { if (this._events[event] == null) { throw new Error('no event found'); } const fun = (listener) => listener(data); this._events[event].forEach(fun); } } const emitter = new MyEmitter(); // 你要使用数据的地方 emitter.on('getRes', (res) => console.log(res)); ... const getRes = async () => { const res = await yourAsyncFun(); emitter.emit('getRes', res); } await getRes(); |
18
shenyu1996 2020-10-25 10:26:11 +08:00 via Android
await 变同步 或者放在异步的回调里
|
19
Arrowing 2020-10-25 11:35:17 +08:00 via Android
把.then 及其后面的代码删掉
|
20
Doracis 2020-10-26 08:55:22 +08:00
请求回来的 result,用方法调用,作为形参,这样试试?
...then((res) => { this.anotherFunc(res) }) |
21
css3 OP |
22
css3 OP |
23
Arrowing 2020-10-27 17:34:20 +08:00
```javascript
function test() { var result = undefined let url = "https://www.v2ex.com/api/topics/latest.json" let res = $.get({ // 假设支持 jQuery,V2ex 控制台下可测试 url: url, async: false // 使用同步请求 }) return res.responseJSON[0]['title'] } ``` |
24
css3 OP @Arrowing jsbox 中,加了 async: false,拿到的 res.responseJSON[0]['title'] ,还是个 Pormise
|
25
css3 OP 罢了罢了,解决不了放弃了
|