js 异步 http 请求的返回值,如何赋值给一个变量?

2020-10-24 21:56:08 +08:00
 css3

异步编程新新新新手, 不应该说编程新手

这几日在用 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 的值
3819 次点击
所在节点    程序员
25 条回复
Yumwey
2020-10-24 21:57:34 +08:00
async/await 返回的是 promise,所以,知道怎么拿了吗?
kuanng
2020-10-24 22:00:24 +08:00
getRet 这个函数返回的是一个 promise 吧
Jirajine
2020-10-24 22:12:13 +08:00
异步就是这样的啊,你要非要用同步的风格写异步,那就把代码逻辑包在一个 async IIFE 里面,像这样
(async function(){
// your code here
})()
然后用 await 就能拿到包在 promise 里面的值了。

还有个极其不推荐的办法是定义一个全局变量,然后异步代码里把结果赋值给这个变量,但你无法保证你取用这个变量的代码被调度到后面执行。
css3
2020-10-24 22:13:51 +08:00
@css3 @Yumwey @kuanng
这样可以,但我后面要用这个返回值的地方,是构建一个 webvirw, 它是不支持直接给一坨东西的,还是得赋值给变量才能用啊
https://gist.github.com/seryte/e725987b2b18ccb43772a2b8ea182198
vision1900
2020-10-24 22:20:27 +08:00
你在写 test 的时候就已经拿到了值,只是这个是异步的
无论你怎么封装,都不可能用同步的方式拿到值
async/await 是一层语法糖,只是写起来像同步的而已,实际还是依赖 promise 。也就是说实际还是异步代码

getRet() 运行的上下文是同步的,一开始方向就搞错了,陷入了“封装陷阱”
css3
2020-10-24 22:22:54 +08:00
@vision1900 😂非常的对,我已经陷入封装循环了,还跳不出来异步这个“坎”
css3
2020-10-24 22:23:36 +08:00
@Jirajine 尝试过,不行的,取不到
css3
2020-10-24 22:23:58 +08:00
@vision1900 所以,我怎么用同步的方法拿到异步的返回值?
Yumwey
2020-10-24 22:32:55 +08:00
你非要同步的话,又不想再套 async/await 走逻辑的话,那就写个 emit
user8341
2020-10-24 22:36:21 +08:00
@css3

异步代码赋给全局变量。同步代码这边 setInterval,然后不断重试最后总能取到。
Jirajine
2020-10-24 22:38:55 +08:00
@css3 你还是没看我在说什么。
把所有代码都包进去,如果用全局变量赋值的话,很可能你后面取用的代码会先于你给全局变量赋值的异步代码执行,所以你会“取不到”,因为你取的时候还没赋值呢。这就是异步。
vision1900
2020-10-24 22:53:35 +08:00
@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();
hazyzh
2020-10-24 23:18:57 +08:00
我怎么用同步的方法拿到异步的返回值?

???
这个问题问的非常好!这需要利用量子力学的一个概念 **协变量子场**, [空间和时间都不是连续的,宇宙时间,世界的一切,包括时空,都只是协变量子场的表现形式] 。

建议你研究一下这个,这个问题一解决 可以从根本上一举攻克 js 回掉地狱的历史性难题。
autoxbc
2020-10-24 23:26:14 +08:00
异步是语言级的特性,没有「不支持这样的格式」的说法
GzhiYi
2020-10-24 23:57:31 +08:00
关键字是:宏任务和微任务
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)
}())
//调用的
```
kwrush
2020-10-25 04:20:58 +08:00
想要同步获取异步结果,那可以自己实现一个简单的 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();
shenyu1996
2020-10-25 10:26:11 +08:00
await 变同步 或者放在异步的回调里
Arrowing
2020-10-25 11:35:17 +08:00
把.then 及其后面的代码删掉
Doracis
2020-10-26 08:55:22 +08:00
请求回来的 result,用方法调用,作为形参,这样试试?
...then((res) => {
this.anotherFunc(res)
})

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/718262

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX