异步编程新新新新手, 不应该说编程新手
这几日在用 jsbox 构建一个自己的小工具,卡在了 http 异步请求这一块,请求返回值死活无法赋值给一个全局的变量给其他地方使用;
了解了异步的一些基础用法,有通过 then 获取的,有通过定义 async 函数获取的,还有回调函数获取的 但发现有个特点就是拿到的返回值,作用域均仍在内部,拿不出来这个返回值
// 调用 v 站的 api 例子,获取最新主题 function test() { var result = undefined let url = "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 = "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 |
![]() | 10 9LCRwvU14033RHJo 2020-10-24 22:36:21 +08:00 |
11 Jirajine 2020-10-24 22:38:55 +08:00 via Android @css3 你还是没看我在说什么。 把所有代码都包进去,如果用全局变量赋值的话,很可能你后面取用的代码会先于你给全局变量赋值的异步代码执行,所以你会“取不到”,因为你取的时候还没赋值呢。这就是异步。 |
12 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 = "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 = "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 = "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 ![]() 我怎么用同步的方法拿到异步的返回值? ??? 这个问题问的非常好!这需要利用量子力学的一个概念 **协变量子场**, [空间和时间都不是连续的,宇宙时间,世界的一切,包括时空,都只是协变量子场的表现形式] 。 建议你研究一下这个,这个问题一解决 可以从根本上一举攻克 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 = "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 0: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(); |
![]() | 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 = "api/topic/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 罢了罢了,解决不了放弃了 |