调用Facebook /me接口后User对象先空后填充,如何直接获取完整对象
问题分析与解决方案
嗨,我来帮你理清这个问题的原因和解决办法~
为什么会出现undefined?
核心原因是**FB.api是异步操作**,JavaScript不会等它执行完再继续往下走。当你的testAPI函数执行到return user时,FB.api的回调函数(就是给user赋值的那段代码)还没被调用呢!这时候user的id和name自然还是undefined。而你看到的后面打印的完整用户数据,是等异步请求完成后,回调执行才赋值的,但这时候testAPI早就把空的user返回出去了。
解决方案
我们需要等异步请求完成、user被填充好之后,再去使用它。这里有两种常用的方式:
方式1:用回调函数处理结果
把需要使用user的逻辑放到FB.api的回调里,或者给testAPI传一个回调函数,等数据准备好再触发:
// 构造函数不变 function User(id, name){ this.id = id; this.name = name; } // FB初始化不变 window.fbAsyncInit = function() { FB.init({ appId: '{my-key}', cookie: true, xfbml: true, version: 'v2.8' }); FB.getLoginStatus(function(response) { statusChangeCallback(response); }); }; // 修改testAPI,接受一个回调函数 function testAPI(callback) { var user = new User(); FB.api('/me', function(response) { user.id = response.id; user.name = response.name; console.log(response); // 数据准备好后,调用回调把user传出去 callback(user); }); } // 修改statusChangeCallback,在回调里处理user function statusChangeCallback(response) { console.log('statusChangeCallback'); console.log(response); if (response.status === 'connected') { $(function() { testAPI(function(user) { console.log(user); // 这里就能拿到完整的User对象了 // 所有需要用到user的逻辑都写在这里 }); }); } else { document.getElementById('status').innerHTML = 'Please log ' + 'into this app.'; } }
方式2:用Promise + async/await(更直观的“同步”写法)
把异步操作包裹成Promise,再用async/await让代码看起来像同步执行:
// 构造函数不变 function User(id, name){ this.id = id; this.name = name; } // FB初始化不变 window.fbAsyncInit = function() { FB.init({ appId: '{my-key}', cookie: true, xfbml: true, version: 'v2.8' }); FB.getLoginStatus(function(response) { statusChangeCallback(response); }); }; // 把testAPI改成返回Promise function testAPI() { return new Promise((resolve, reject) => { var user = new User(); FB.api('/me', function(response) { if (response.error) { // 请求失败时抛出错误 reject(response.error); return; } user.id = response.id; user.name = response.name; console.log(response); // 请求成功时返回填充好的user resolve(user); }); }); } // 把statusChangeCallback改成async函数,用await等待结果 async function statusChangeCallback(response) { console.log('statusChangeCallback'); console.log(response); if (response.status === 'connected') { $(async function() { try { var user = await testAPI(); console.log(user); // 这里拿到的就是完整的User对象 } catch (error) { console.error('获取用户信息失败:', error); } }); } else { document.getElementById('status').innerHTML = 'Please log ' + 'into this app.'; } }
总结
JavaScript的异步操作不会阻塞代码执行,所以不能在异步操作外面直接返回还没准备好的数据。必须等异步回调完成(或者Promise resolve)后,再去处理填充好的对象。上面两种方式都能解决你的问题,你可以根据自己的习惯选择~
内容的提问来源于stack exchange,提问作者BozZ
相关产品推荐
相关产品推荐

