如何用异步值填充对象?解决Promise未自动解析问题
问题描述
我有一个包含多个属性的对象,其中部分属性的值为Promise。我希望等待所有Promise完成解析后,让这些属性直接存储Promise的解析值。
目前我编写了如下代码,但对象o的o3、o4、o5属性仍然保留着已完成的Promise,而非解析后的实际值:
var f_o_data = function(b_reject){ return new Promise( function( f_resolve, f_reject ){ window.setTimeout(function(){ var n_ts_ms = parseInt(new Date().getTime()); // if(n_ts_ms % 2 == 0){ // f_reject(`i rejected because n_ts_ms ${n_ts_ms} % 2 == 0 is true :shrug:`) // } if(b_reject){ f_reject('i rejected because true was passed to function') } f_resolve( { n_ts_ms: n_ts_ms, s: 'i am the result object', n: 23, b: true, a_n: [1,2,3], a_o:[{n:2, n:3}, {n:3}] } ) },parseInt(Math.random()*3333+555)) } ) } var f_o__filled_asyncally = async function(){ var o = { b: true, n: 2, s: 'just a string', o: {n:22}, a_n: [1,2,3], a_o:[{n:2, n:3}, {n:3}], o1: await f_o_data(),// 这两个异步函数会按顺序执行,因为用了await o2: await f_o_data(),// o3: f_o_data(),// 这三个可以并行加载 o4: f_o_data().then(function(o){return o}),// 这三个可以并行加载 o5: f_o_data(),// 这三个可以并行加载 // o6: f_o_data(true),// 这三个可以并行加载 } return Promise.all(Object.values(o)).then( function(a_o_promise){ return o } ) // console.log(o) // console.log(o.o3) // console.log(o.o4) // console.log(o.o5) // return o; } var o_my_object_i_want_to_fill_asyncally = await f_o__filled_asyncally(); console.log(o_my_object_i_want_to_fill_asyncally)
解决方案
当前代码里,Promise.all(Object.values(o))只是等待所有Promise完成,但没有把解析后的结果赋值回原对象的对应属性。要实现需求,需要保留对象键和值的关联关系,等所有Promise解析完成后将结果映射回对象:
var f_o_data = function(b_reject){ return new Promise(function(f_resolve, f_reject){ window.setTimeout(function(){ var n_ts_ms = parseInt(new Date().getTime()); if(b_reject){ f_reject('i rejected because true was passed to function') } f_resolve({ n_ts_ms: n_ts_ms, s: 'i am the result object', n: 23, b: true, a_n: [1,2,3], a_o:[{n:3}, {n:3}] // 原代码中重复键会被覆盖,此处修正 }) }, parseInt(Math.random()*3333+555)) }) } var f_o__filled_asyncally = async function(){ // 先创建对象,让o3、o4、o5立即启动并行请求 var o = { b: true, n: 2, s: 'just a string', o: {n:22}, a_n: [1,2,3], a_o:[{n:2, n:3}, {n:3}], o1: await f_o_data(), // 顺序执行 o2: await f_o_data(), // 顺序执行 o3: f_o_data(), // 并行执行 o4: f_o_data(), // 并行执行(原代码的then(o=>o)无意义,直接移除) o5: f_o_data() // 并行执行 } // 将对象转为[key, value]格式的数组,保留键值对应关系 const entries = Object.entries(o); // 等待所有值(包括普通值和Promise)解析完成 const resolvedValues = await Promise.all(entries.map(([key, value]) => value)); // 把解析后的结果赋值回原对象的对应属性 entries.forEach(([key], index) => { o[key] = resolvedValues[index]; }); return o; } var o_my_object_i_want_to_fill_asyncally = await f_o__filled_asyncally(); console.log(o_my_object_i_want_to_fill_asyncally)
关键说明
Object.entries(o)把对象转换成键值对数组,确保后续能准确对应每个属性的解析结果Promise.all会自动处理普通值(直接返回)和Promise(等待解析),无需额外判断- 遍历键值对数组,将解析后的结果逐一赋值回原对象,完成属性替换
- 原代码中
o4的.then(function(o){return o})属于冗余代码,可直接删除,不影响功能 - 注意原代码中
a_o数组内的第一个对象有重复键n,JavaScript会自动保留最后一个值,建议编写时避免这种情况
内容的提问来源于stack exchange,提问作者Jonas Frey
相关产品推荐
相关产品推荐

