V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
css3
V2EX  ›  程序员

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

  •  
  •   css3 · 2020-10-24 21:56:08 +08:00 · 4035 次点击
    这是一个创建于 1496 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    这几日在用 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 的值
    
    第 1 条附言  ·  2020-10-26 11:05:08 +08:00

    还是交待一下上下文吧,我的原始需求: 我在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的返回值传递过去?正确的做法是什么?

    25 条回复    2020-10-27 21:50:34 +08:00
    Yumwey
        1
    Yumwey  
       2020-10-24 21:57:34 +08:00 via Android
    async/await 返回的是 promise,所以,知道怎么拿了吗?
    kuanng
        2
    kuanng  
       2020-10-24 22:00:24 +08:00
    getRet 这个函数返回的是一个 promise 吧
    Jirajine
        3
    Jirajine  
       2020-10-24 22:12:13 +08:00 via Android
    异步就是这样的啊,你要非要用同步的风格写异步,那就把代码逻辑包在一个 async IIFE 里面,像这样
    (async function(){
    // your code here
    })()
    然后用 await 就能拿到包在 promise 里面的值了。

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

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

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

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

    建议你研究一下这个,这个问题一解决 可以从根本上一举攻克 js 回掉地狱的历史性难题。
    autoxbc
        14
    autoxbc  
       2020-10-24 23:26:14 +08:00
    异步是语言级的特性,没有「不支持这样的格式」的说法
    GzhiYi
        15
    GzhiYi  
       2020-10-24 23:57:31 +08:00
    关键字是:宏任务和微任务
    aaronlam
        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)
    }())
    //调用的
    ```
    kwrush
        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();
    shenyu1996
        18
    shenyu1996  
       2020-10-25 10:26:11 +08:00 via Android
    await 变同步 或者放在异步的回调里
    Arrowing
        19
    Arrowing  
       2020-10-25 11:35:17 +08:00 via Android
    把.then 及其后面的代码删掉
    Doracis
        20
    Doracis  
       2020-10-26 08:55:22 +08:00
    请求回来的 result,用方法调用,作为形参,这样试试?
    ...then((res) => {
    this.anotherFunc(res)
    })
    css3
        21
    css3  
    OP
       2020-10-27 15:48:55 +08:00
    css3
        22
    css3  
    OP
       2020-10-27 15:51:00 +08:00
    @vision1900
    @aaronlam
    @kwrush
    @Doracis
    @Arrowing
    @shenyu1996
    大佬帮忙看一下,我 append 了原始需求
    Arrowing
        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']
    }
    ```
    css3
        24
    css3  
    OP
       2020-10-27 21:21:08 +08:00
    @Arrowing jsbox 中,加了 async: false,拿到的 res.responseJSON[0]['title'] ,还是个 Pormise
    css3
        25
    css3  
    OP
       2020-10-27 21:50:34 +08:00
    罢了罢了,解决不了放弃了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1529 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 17:10 · PVG 01:10 · LAX 09:10 · JFK 12:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.