我有一个深刻的对象:
{
"something": "Homepage",
"else": [
"[replaceme]",
"[replaceme]"
],
"aside": "[replaceme]",
"test": {
"test": {
"testing": [
"[replaceme]",
"[replaceme]",
"variable",
{
"testing": {
"testing": {
"something": "[replaceme]",
"testing": {
"testing": [
"[replaceme]",
"[replaceme]"
]
}
}
}
}
]
}
}
}
现在我需要用异步函数中的某些内容替换[replaceme]
的每一个匹配项。每次都不一样。
我以为我减少了对象的每个级别并返回了一个promise链。
这是我到目前为止所得到的:
const IterateObject = ( object ) => {
return new Promise( ( resolve, reject ) => {
Object.keys( object ).reduce( ( sequence, current ) => {
const key = current;
return sequence.then( () => {
return new Promise( ( resolve, reject ) => {
if( typeof object[ key ] === 'object' ) {
IterateObject( object[ key ] )
.then( result => {
newObject[ key ] = result;
});
// ^----- How do I add the next level when it returns a promise?
}
else {
resolve( newObject[ key ] )
}
});
});
}, Promise.resolve())
.catch( error => reject( error ) )
.then( () => {
console.log('done');
resolve();
});
});
}
解决此问题的最佳方法是什么?也许承诺链不是正确的工具?
答案 0 :(得分:2)
不要尝试单一功能。这可能导致常见的反模式知道为The Collection Kerfuffle将此作业拆分为独立的块:深度遍历对象,异步设置器等。通过遍历对象收集所有挂起的promise并使用Promise.all
<等待它们全部/ p>
// traverse object obj deep using fn iterator
const traverse = (obj, fn) => {
const process = (acc, value, key, object) => {
const result =
Array.isArray(value)
? value.map((item, index) => process(acc, item, index, value))
: (
typeof value === 'object'
? Object.keys(value).map(key => process(acc, value[key], key, value))
: [fn(value, key, object)]
)
return acc.concat(...result)
}
return process([], obj)
}
// fake async op
const getAsync = value => new Promise(resolve => setTimeout(resolve, Math.random()*1000, value))
// useful async setter
const setAsync = (target, prop, pendingValue) => pendingValue.then(val => target[prop] = val)
// traverse object obj deep using fn iterator
const traverse = (obj, fn) => {
const process = (acc, value, key, object) => {
const result =
Array.isArray(value)
? value.map((item, index) => process(acc, item, index, value))
: (
typeof value === 'object'
? Object.keys(value).map(key => process(acc, value[key], key, value))
: [fn(value, key, object)]
)
return acc.concat(...result)
}
return process([], obj)
}
// set async value
const replace = (value, prop, target) => {
if( value === '[replaceme]') {
return setAsync(target, prop, getAsync(`${prop} - ${Date.now()}`))
}
return value
}
const tmpl = {
"something": "Homepage",
"else": [
"[replaceme]",
"[replaceme]"
],
"aside": "[replaceme]",
"test": {
"test": {
"testing": [
"[replaceme]",
"[replaceme]",
"variable",
{
"testing": {
"testing": {
"something": "[replaceme]",
"testing": {
"testing": [
"[replaceme]",
"[replaceme]"
]
}
}
}
}
]
}
}
}
Promise.all(
traverse(tmpl, replace)
)
.then(() => console.log(tmpl))
.catch(e => console.error(e))
&#13;
或者你可能想看一下async/await
,它可以让你写更多&#34;同步和#34;码。但是这种实现会导致顺序执行。
// using async/await
const fillTemplate = async (tmpl, prop, object) => {
if(Array.isArray(tmpl)) {
await Promise.all(tmpl.map((item, index) => fillTemplate(item, index, tmpl)))
}
else if(typeof tmpl === 'object') {
await Promise.all(Object.keys(tmpl).map(key => fillTemplate(tmpl[key], key, tmpl)))
}
else {
await replace(tmpl, prop, object)
}
}
const tmpl = {
"something": "Homepage",
"else": [
"[replaceme]",
"[replaceme]"
],
"aside": "[replaceme]",
"test": {
"test": {
"testing": [
"[replaceme]",
"[replaceme]",
"variable",
{
"testing": {
"testing": {
"something": "[replaceme]",
"testing": {
"testing": [
"[replaceme]",
"[replaceme]"
]
}
}
}
}
]
}
}
}
// fake async op
const getAsync = value => new Promise(resolve => setTimeout(resolve, Math.random()*1000, value))
// useful async setter
const setAsync = (target, prop, pendingValue) => pendingValue.then(val => target[prop] = val)
// set async value
const replace = (value, prop, target) => {
if( value === '[replaceme]') {
return setAsync(target, prop, getAsync(`${prop} - ${Date.now()}`))
}
return value
}
// using async/await
const fillTemplate = async (tmpl, prop, object) => {
if(Array.isArray(tmpl)) {
await Promise.all(tmpl.map((item, index) => fillTemplate(item, index, tmpl)))
}
else if(typeof tmpl === 'object') {
await Promise.all(Object.keys(tmpl).map(key => fillTemplate(tmpl[key], key, tmpl)))
}
else {
await replace(tmpl, prop, object)
}
}
Promise
.resolve(fillTemplate(tmpl))
.then(() => console.log(tmpl))
.catch(e => console.error(e))
&#13;
答案 1 :(得分:1)
我认为这与你要做的事情是一致的。
var object =
{
"something": "Homepage",
"else": [
"[replaceme]",
"[replaceme]"
],
"aside": "[replaceme]",
"test": {
"test": {
"testing": [
"[replaceme]",
"[replaceme]",
"variable",
{
"testing": {
"testing": {
"something": "[replaceme]",
"testing": {
"testing": [
"[replaceme]",
"[replaceme]"
]
}
}
}
}
]
}
}
}
function parseObject(object, promises){
var keys = Object.keys(object);
for(let i = 0; i < keys.length; i++){
let item = object[keys[i]];
if(item instanceof Array){
for(let k = 0;k < item.length; k++){
if(typeof(item[k]) === "object") {
parseObject(item[k], promises);
}
else{
promises.push(
magicFunction(
item[k],
(newValue) => item[k] = newValue
)
);
}
}
}
else if(typeof(item) === "object")
parseObject(item, promises);
else
promises.push(magicFunction(item , (newValue) => object[keys[i]] = newValue));
}
return;
}
function magicFunction(item, defferedAssignment){
return new Promise(( resolve, reject ) => {
if(item === "[replaceme]") {
// Some ajax or misc
setTimeout(() => {
defferedAssignment("Replaced");
resolve()
}, Math.floor(Math.random() * 1000));
}
else {
defferedAssignment(item);
resolve()
}
});
}
var promises = [];
parseObject(object, promises);
Promise.all(promises).then(() => console.log(object))
我不知道这是否是最好的方法,但它对我有用:)。
你也可以将promiseAll包装成一个函数。
此外,这还要求magicFunction的结果不会返回另一个[replaceme]。如果这不是所需的选项,则需要进行微调。