-
Notifications
You must be signed in to change notification settings - Fork 538
Open
Description
Problem
flow silently breaks when any composed function returns a Promise. It passes the raw Promise object to the next function instead of the resolved value:
const fetchUser = async (id: number) => db.users.find(id);
const getName = (user: User) => user.name;
const getUserName = flow(fetchUser, getName);
await getUserName(1); // TypeError: Cannot read property 'name' of [object Promise]The implementation has no await:
// current flow implementation
for (let i = 1; i < funcs.length; i++) {
result = funcs[i].call(this, result); // passes raw Promise
}Proposal
const getUserName = flowAsync(fetchUser, getName);
await getUserName(1); // "Alice"Implementation is flow + async + two awaits:
export function flowAsync(...funcs) {
return async function (this: any, ...args: any[]) {
let result = funcs.length ? await funcs[0].apply(this, args) : args[0];
for (let i = 1; i < funcs.length; i++) {
result = await funcs[i].call(this, result);
}
return result;
};
}Precedent
es-toolkit already follows a sync/Async naming convention:
attempt/attemptAsyncfilter/filterAsyncflatMap/flatMapAsyncmap/mapAsyncreduce/reduceAsync
flow / flowAsync fits this pattern exactly.
The TC39 proposal-function-pipe-flow included Function.flowAsync but was withdrawn at Stage 1, with the committee noting these are "easily solved by userland functions" — which is what es-toolkit provides.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels