json 里的value 如何从不同的上下文中取值-之promise

106 阅读3分钟

如题

在文章 json 里的value 如何从不同的上下文中取值 中介绍了从静态数据源里获取json。

怎么从 api 获取呢,这样就将数据源扩展到任何数据了。

从api 获取,其实是异步获取,这里要用到promise

实现

改动的代码不多,把两个方法 promise 化就可以实现

第一方法 replaceParams

原方法为

function replaceParams(params, current_data_source) {
    if (test.is_array(params)) {
        params = params.map(param=>replaceParams(param, current_data_source))
    }
    else if (test.is_object(params)) {
        if (params._data_source) {
            params = replaceParams(params.key, getDataSource(params))
        } else {
            if (test.is_array(current_data_source)) {
                params = current_data_source.map(item=> replaceParams(JSON.parse(JSON.stringify(params)), item))
            } else {
                Object.keys(params).map(key => params[key]=replaceParams(params[key], current_data_source))
            }
            
        }
    }
    else if (test.is_string(params)) {
        params = replaceParam(params, current_data_source)
    }
    
    return params
}

async后为

async function replaceParams(params, current_data_source) {
    if (test.is_function(current_data_source)) {
        do {
            current_data_source = await current_data_source()
        } while (test.is_function(current_data_source))
        // 看下是否是动态数据源
        if (test.is_object(current_data_source) && current_data_source._is_dynamic) {
            current_data_source = await replaceParams(current_data_source.data || [])
        }

    }

    if (test.is_array(params)) {
        params = await Promise.all(params.map(async param => await replaceParams(param, current_data_source)))
    }
    else if (test.is_object(params)) {
        if (params._data_source) {
            params = await replaceParams(params.key, getDataSource(params))
        } else {
            if (test.is_array(current_data_source)) {
                params = await Promise.all(current_data_source.map(async item => await replaceParams(JSON.parse(JSON.stringify(params)), item)))
            } else {
                await Promise.all(Object.keys(params).map(async key => params[key] = await replaceParams(params[key], current_data_source)))
            }
        }
    }
    else if (test.is_string(params)) {
        params = await replaceParam(params, current_data_source)
    }

    return params
}

函数内添加了几行

if (test.is_function(current_data_source)) {
    do {
        current_data_source = await current_data_source()
    } while (test.is_function(current_data_source))
    // 看下是否是动态数据源
    if (test.is_object(current_data_source) && current_data_source._is_dynamic) {
        current_data_source = await replaceParams(current_data_source.data || [])
    }

}

预先把数据源获取出来,假如数据源是一个动态数据源(即数据源里面也是从其他地方获取的)

第二个方法 replaceParam

原方法

function replaceParam(param, obj) {
    
    if (!test.is_string(param)) {
        return param
    }
    
    if (param.indexOf(':') < 0) {
		return param;
	}
	
	if (param.startsWith(':')) {
		console.log('replaceParam', param, '->', data_get(obj, param.slice(1)))
		return data_get(obj, param.slice(1));
	}
	return param.replace(/:([\w\.]+)/g, function (match, key) {
		console.log('replaceParam', ':'+key, '->', data_get(obj, key))
		return data_get(obj, key);
	});
}

async 后为

async function replaceParam(param, obj) {

    if (!test.is_string(param)) {
        return param
    }

    if (param.indexOf(':') < 0) {
        return param;
    }

    if (typeof obj === 'function') {
        obj = await obj()
    }

    if (param.startsWith(':')) {
        console.log('replaceParam', param, '->', data_get(obj, param.slice(1)))
        return data_get(obj, param.slice(1));
    }
    return param.replace(/:([\w\.]+)/g, function (match, key) {
        console.log('replaceParam', ':' + key, '->', data_get(obj, key))
        return data_get(obj, key);
    });
}

代码里多了三行,主要是为了适配数据源是一个函数,这个函数,可以直接返回对象,也可以返回一个promise,上方预先把函数拦截掉了,这个不要也可以。但作为一个独立的方法,实现它更灵活一点(注意,只支持一层函数)

if (typeof obj === 'function') {
        obj = await obj()
    }

所有代码为

var user = {
    id: 1
}

var book = {
    id: 2
}

var address = {
    id: 3
}
var data_sources = {}

data_sources.user = user
data_sources.book = book
data_sources.address = address

function getDataSource(config) {
    return data_sources[config._data_source] || data_sources
}


var test = {
    is_array: function (value) {
        if (typeof Array.isArray === 'function') {
            return Array.isArray(value)
        }
        return Object.prototype.toString.call(value) === '[object Array]'
    },
    is_object: function (value) {
        return Object.prototype.toString.call(value) === '[object Object]'
    },
    is_string: function (value) {
        return typeof value === 'string'
    },
    is_function: function (value) {
        return typeof value === 'function'
    }
}

async function replaceParams(params, current_data_source) {
    if (test.is_function(current_data_source)) {
        do {
            current_data_source = await current_data_source()
        } while (test.is_function(current_data_source))
        // 看下是否是动态数据源
        if (test.is_object(current_data_source) && current_data_source._is_dynamic) {
            current_data_source = await replaceParams(current_data_source.data || [])
        }
    
    }
    
    if (test.is_array(params)) {
        params = await Promise.all(params.map(async param => await replaceParams(param, current_data_source)))
    }
    else if (test.is_object(params)) {
        if (params._data_source) {
            params = await replaceParams(params.key, getDataSource(params))
        } else {
            if (test.is_array(current_data_source)) {
                params = await Promise.all(current_data_source.map(async item => await replaceParams(JSON.parse(JSON.stringify(params)), item)))
            } else {
                await Promise.all(Object.keys(params).map(async key => params[key] = await replaceParams(params[key], current_data_source)))
            }
        }
    }
    else if (test.is_string(params)) {
        params = await replaceParam(params, current_data_source)
    }

    return params
}

async function replaceParam(param, obj) {

    if (!test.is_string(param)) {
        return param
    }

    if (param.indexOf(':') < 0) {
        return param;
    }

    if (typeof obj === 'function') {
        obj = await obj()
    }

    if (param.startsWith(':')) {
        console.log('replaceParam', param, '->', data_get(obj, param.slice(1)))
        return data_get(obj, param.slice(1));
    }
    return param.replace(/:([\w\.]+)/g, function (match, key) {
        console.log('replaceParam', ':' + key, '->', data_get(obj, key))
        return data_get(obj, key);
    });
}

function data_get(target, path, fallback) {
    let segments = Array.isArray(path) ? path : path.split('.');
    let [segment] = segments;

    let find = target;

    if (segment !== '*' && segments.length > 0) {
        if (find[segment] === null || typeof find[segment] === 'undefined') {
            find = typeof fallback === 'function' ? fallback() : fallback;
        }
        else {
            find = data_get(find[segment], segments.slice(1), fallback);
        }
    }

    else if (segment === '*') {
        const partial = segments.slice(path.indexOf('*') + 1, path.length);

        if (typeof find === 'object') {
            find = Object.keys(find).reduce((build, property) => ({
                ...build,
                [property]: data_get(find[property], partial, fallback)
            }),
                {});
        }
        else {
            find = data_get(find, partial, fallback);
        }
    }


    /*-----------------------------------------------------------------------------
     |   Arrayable Requirements
     *-----------------------------------------------------------------------------
     |
     |   . All arrays are converted to objects
     |   . For Example
     |      #Code
     |        Code -> data_set({ list: ['one', 'two', 'three'], 'list.*', 'update', true });
     |
     |      #Input
     |         Input -> { list: ['one', 'two', 'three'] }
     |
     |      #During We Convert Arrays To "Indexed Objects"
     |         During -> { list: { '1': 'one', '2': 'two', '3': 'three' } }
     |
     |      #Before Output we convert "Indexed Objects" Back To Arrays
     |         From -> { list: { '1': 'update', '2': 'update', '3': 'update' } }
     |         Into -> { list: ['update', 'update', 'update'] }
     |
     |   . Arrays convert into "Indexed Objects", allowing for wildcard (*) capabilities
     |   . "Indexed Objects" are converted back into arrays before returning the updated target
     |
     */
    if (typeof find === 'object') {
        if (Object.prototype.toString.call(find) === '[object Array]') {
            if (find.length == 0) {
                return find
            }
        }
        if (Object.keys(find).length > 0) {
            const isArrayTransformable = Object.keys(find).every(index => index.match(/^(0|[1-9][0-9]*)$/));

            return isArrayTransformable ? Object.values(find) : find;
        }
    } else {
        return find;
    }
};

验证

data_sources.users = [
    {
        id: 1,
        name: "11"
    },
    {
        id: 2,
        name: "22"
    }
]
data_sources.post = {
    id: 4,
    comment: {
        id: 5
    }
}


data_sources.promise = function () {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({
                id: 6
            })
        }, 5000)
    })
}
data_sources.promise1 = function () {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({
                id: 7
            })
        }, 2000)
    })
}

replaceParams({
    'promise_id': {
        "_data_source": "promise",
        "key": {
            "id": ":id"
        }
    },
    'promise1_id': {
        "_data_source": "promise1",
        "key": {
            "id": ":id"
        }
    },
    'test': [
        ":user.id",
        ":book.id",
        [
            ":user.id",
            ":book.id",
            ":address"
        ]
    ],
    "users": ":users",
    "_map_users": {
        "_data_source": "users",
        "key": {
            "name": ":name"
        }
    },
    "user_ids": ":users.*.id",
    "extra": {
        "user_ids": ":users.*.id",
        "_user_ids": {
            "_data_source": "users",
            "key": ":*.id"
        },
        "post": {
            "_data_source": "post",
            "key": {
                "post_id": ":id",
                "comment": ":comment",
                "comment_id": ":comment.id",
                "_user_ids": {
                    "_data_source": "users",
                    "key": ":*.id"
                }
            }
        }
    }
}, data_sources).then(params=>console.log(JSON.stringify(params))).catch(error=>{console.log(error)})
// 5秒后 ouput {"promise_id":{"id":6},"promise1_id":{"id":7},"test":[1,2,[1,2,{"id":3}]],"users":[{"id":1,"name":"11"},{"id":2,"name":"22"}],"_map_users":[{},{}],"user_ids":[1,2],"extra":{"user_ids":[1,2],"_user_ids":[1,2],"post":{"post_id":4,"comment":{"id":5},"comment_id":5,"_user_ids":[1,2]}}}