Mat*_*att 22 javascript asynchronous promise
我一直在为我自己使用的小型2D游戏库工作,我遇到了一些问题.库中有一个名为loadGame的特定函数,它将依赖信息作为输入(资源文件和要执行的脚本列表).这是一个例子.
loadGame({
"root" : "/source/folder/for/game/",
"resources" : {
"soundEffect" : "audio/sound.mp3",
"someImage" : "images/something.png",
"someJSON" : "json/map.json"
},
"scripts" : [
"js/helperScript.js",
"js/mainScript.js"
]
})
Run Code Online (Sandbox Code Playgroud)
资源中的每个项目都有一个密钥,游戏使用该密钥来访问该特定资源.loadGame函数将资源转换为promises对象.
问题是它试图使用Promises.all来检查它们何时准备就绪,但是Promise.all只接受迭代作为输入 - 所以像我所拥有的对象是不可能的.
所以我尝试将对象转换为数组,这很有效,除了每个资源只是数组中的一个元素,并且没有用于标识它们的键.
这是loadGame的代码:
var loadGame = function (game) {
return new Promise(function (fulfill, reject) {
// the root folder for the game
var root = game.root || '';
// these are the types of files that can be loaded
// getImage, getAudio, and getJSON are defined elsewhere in my code - they return promises
var types = {
jpg : getImage,
png : getImage,
bmp : getImage,
mp3 : getAudio,
ogg : getAudio,
wav : getAudio,
json : getJSON
};
// the object of promises is created using a mapObject function I made
var resources = mapObject(game.resources, function (path) {
// get file extension for the item
var extension = path.match(/(?:\.([^.]+))?$/)[1];
// find the correct 'getter' from types
var get = types[extension];
// get it if that particular getter exists, otherwise, fail
return get ? get(root + path) :
reject(Error('Unknown resource type "' + extension + '".'));
});
// load scripts when they're done
// this is the problem here
// my 'values' function converts the object into an array
// but now they are nameless and can't be properly accessed anymore
Promise.all(values(resources)).then(function (resources) {
// sequentially load scripts
// maybe someday I'll use a generator for this
var load = function (i) {
// load script
getScript(root + game.scripts[i]).then(function () {
// load the next script if there is one
i++;
if (i < game.scripts.length) {
load(i);
} else {
// all done, fulfill the promise that loadGame returned
// this is giving an array back, but it should be returning an object full of resources
fulfill(resources);
}
});
};
// load the first script
load(0);
});
});
};
Run Code Online (Sandbox Code Playgroud)
理想情况下,我想以某种方式正确管理资源的承诺列表,同时仍然保留每个项目的标识符.任何帮助将不胜感激,谢谢.
小智 12
如果你使用lodash库,你可以通过单行函数来实现:
Promise.allValues = async (object) => {
return _.zipObject(_.keys(object), await Promise.all(_.values(object)))
}
Run Code Online (Sandbox Code Playgroud)
Ber*_*rgi 11
首先:废弃Promise
构造函数,这个用法是反模式的!
现在,针对您的实际问题:正确识别后,您错过了每个值的关键字.您需要在每个promise中传递它,以便在等待所有项目后重建对象:
function mapObjectToArray(obj, cb) {
var res = [];
for (var key in obj)
res.push(cb(obj[key], key));
return res;
}
return Promise.all(mapObjectToArray(input, function(arg, key) {
return getPromiseFor(arg, key).then(function(value) {
return {key: key, value: value};
});
}).then(function(arr) {
var obj = {};
for (var i=0; i<arr.length; i++)
obj[arr[i].key] = arr[i].value;
return obj;
});
Run Code Online (Sandbox Code Playgroud)
像Bluebird这样的更强大的库也会将其作为辅助函数提供Promise.props
.
此外,您不应该使用该伪递归load
函数.您可以简单地将承诺链接在一起:
….then(function (resources) {
return game.scripts.reduce(function(queue, script) {
return queue.then(function() {
return getScript(root + script);
});
}, Promise.resolve()).then(function() {
return resources;
});
});
Run Code Online (Sandbox Code Playgroud)
我实际上为此创建了一个库并将其发布到 github 和 npm:
https://github.com/marcelowa/promise-all-properties
https://www.npmjs.com/package/promise-all-properties
唯一的事情是您需要为对象中的每个承诺分配一个属性名称......这是自述文件中的一个示例
import promiseAllProperties from 'promise-all-properties';
const promisesObject = {
someProperty: Promise.resolve('resolve value'),
anotherProperty: Promise.resolve('another resolved value'),
};
const promise = promiseAllProperties(promisesObject);
promise.then((resolvedObject) => {
console.log(resolvedObject);
// {
// someProperty: 'resolve value',
// anotherProperty: 'another resolved value'
// }
});
Run Code Online (Sandbox Code Playgroud)
这是 @Matt 的答案,其中包含一些类型和一些重命名,并使用ECMA-2019 Object.fromEntries
。
// delayName :: (k, Promise a) -> Promise (k, a)
const delayName = ([name, promise]) => promise.then((result) => [name, result]);
export type PromiseValues<TO> = {
[TK in keyof TO]: Promise<TO[TK]>;
};
// promiseObjectAll :: {k: Promise a} -> Promise {k: a}
export const promiseObjectAll = <T>(object: PromiseValues<T>): Promise<T> => {
const promiseList = Object.entries(object).map(delayName);
return Promise.all(promiseList).then(Object.fromEntries);
};
Run Code Online (Sandbox Code Playgroud)
function resolveObject(obj) {
return Promise.all(
Object.entries(obj).map(async ([k, v]) => [k, await v])
).then(Object.fromEntries);
}
Run Code Online (Sandbox Code Playgroud)
感谢 Cyril Auburtin 的天才:https://esdiscuss.org/topic/modify-promise-all-to-accept-an-object-as-a-parameter
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function test() {
console.time(1);
console.log(await resolveObject({
foo: delay(5).then(()=>1),
bar: delay(120).then(()=>2)
}));
console.timeEnd(1);
}
Run Code Online (Sandbox Code Playgroud)
这是一个简单的 ES2015 函数,它接受一个具有可能是 Promise 的属性的对象,并返回具有已解析属性的该对象的 Promise。
function promisedProperties(object) {
let promisedProperties = [];
const objectKeys = Object.keys(object);
objectKeys.forEach((key) => promisedProperties.push(object[key]));
return Promise.all(promisedProperties)
.then((resolvedValues) => {
return resolvedValues.reduce((resolvedObject, property, index) => {
resolvedObject[objectKeys[index]] = property;
return resolvedObject;
}, object);
});
}
Run Code Online (Sandbox Code Playgroud)
用法:
promisedProperties({a:1, b:Promise.resolve(2)}).then(r => console.log(r))
//logs Object {a: 1, b: 2}
class User {
constructor() {
this.name = 'James Holden';
this.ship = Promise.resolve('Rocinante');
}
}
promisedProperties(new User).then(r => console.log(r))
//logs User {name: "James Holden", ship: "Rocinante"}
Run Code Online (Sandbox Code Playgroud)
请注意,@Bergi 的答案将返回一个新对象,而不是改变原始对象。如果您确实想要一个新对象,只需将传递到reduce函数的初始值设定项值更改为{}
归档时间: |
|
查看次数: |
27834 次 |
最近记录: |