[英]lodash mapValues async
在 _.mapValues 内部,我想获得一些具有一定延迟的修改值(例如来自数据库),但我遇到了问题:当我在同步模式下修改值时,一切都很好,当我尝试使用 promises 或错误地回调它的工作时(在第一种情况我得到 Promise object,第二种情况:未定义值)。 这里有一些简化的例子,我如何重写 mapValues 中的代码来解决这个问题?
'use strict';
const _ = require('lodash');
const Promise = require('bluebird');
let obj = {
one: 1,
two: 2,
};
let increment = (value) => {
return value + 1;
};
let incrementProm = (value) => {
return Promise.resolve(value + 1);
};
let incrementCb = (value, cb) => {
let res = value + 1;
let err = null;
setTimeout(cb.bind(undefined, err, res), 100);
};
let t1 = _.mapValues(obj, (value) => {
return increment(value);
});
let t2 = _.mapValues(obj, (value) => {
return incrementProm(value);
});
let t3 = _.mapValues(obj, (value) => {
let temp;
incrementCb(value, (err, res) => {
temp = res;
});
return temp;
});
console.log('Sync res:');
console.log(t1);
console.log('Promise res:');
console.log(t2);
console.log('Callback res:');
console.log(t3);
您可以使用 bluebird 的props()函数来解析所有带有 Promise 的属性。
Promise.props(_.mapValues(obj, incrementProm))
.then(result => console.log(result));
var obj = { one: 1, two: 2 }; var incrementProm = value => Promise.resolve(value + 1); Promise.props(_.mapValues(obj, incrementProm)) .then(result => console.log(result));
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.4.1/bluebird.js"></script>
您正在映射到承诺,所以我做的事情可能是:
var _ = require('lodash');
let incrementProm = (value) => {
return Promise.resolve(value + 1);
};
let obj = {
foo: 1,
bar: 2
}
let keys = _.keys(obj);
let promises = _.map(keys, k => {
return incrementProm(obj[k])
.then(newValue => { return { key: k, value: newValue } });
})
Promise.all(promises).then(values => {
values.forEach(v => obj[v.key] = v.value)
})
.then(() => {
// now your object is updated: foo = 2 and bar = 3
console.log(obj);
});
您可以实现 mapValues 的异步版本。 有点像...
async function mapValuesAsync(object, asyncFn) {
return Object.fromEntries(
await Promise.all(
Object.entries(object).map(async ([key, value]) => [
key,
await asyncFn(value, key, object)
])
)
);
}
然后像调用_.mapValues
一样调用它:
let t2 = await mapValuesAsync(obj, (value) => {
return incrementProm(value);
});
或者,简单地
let t2 = await mapValuesAsync(obj, incrementProm);
这是一个TypeScript解决方案,它建立在lodash函数之上:
import { fromPairs, toPairs, ObjectIterator } from 'lodash';
export async function mapValuesAsync<T extends object, TResult>(
obj: T | null | undefined,
callback: ObjectIterator<T, Promise<TResult>>,
): Promise<{ [P in keyof T]: TResult }> {
return fromPairs(
await Promise.all(
toPairs(obj).map(async ([key, value]) => [
key,
await callback(value, key, obj),
]),
),
) as { [P in keyof T]: TResult };
}
如果你不需要类型,这里是JavaScript版本:
import { fromPairs, toPairs } from 'lodash';
export async function mapValuesAsync(obj, callback) {
return fromPairs(
await Promise.all(
toPairs(obj).map(async ([key, value]) => [
key,
await callback(value, key, obj),
]),
),
);
}
这是一个测试,以备不时之需:
import { mapValuesAsync } from './map-values-async';
describe('mapValuesAsync', () => {
it('should map values with an async callback', async () => {
const result = await mapValuesAsync(
{
a: 1,
b: 2,
},
async (value) => {
return await Promise.resolve(value * 2);
},
);
const expected = {
a: 2,
b: 4,
};
expect(result).toEqual(expected);
});
});
这受到上面发布的Rui Castro回复的启发,它优雅地使用了Object.entries
和Object.fromEntries
。 但是,这需要 es2019 或更高版本才能工作。 Lodash 具有等效函数toPairs
和fromPairs
,它们适用于 JavaScript 的任何类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.