目前的行为非常令人惊讶:
await async.parallel({foo: async()=>Promise.all([1,2])})
// { foo: [ 1, 2 ] }
await async.parallel({foo: ()=>Promise.all([1,2])})
// waits forever because non-async function expects a ballback
因为对于短函数,很容易只写foo: () => doSomething()
。 很容易忘记async
关键字
但是目前这行不通,需要写成foo: async () => doSomething()
我们是否可以检查回调是否通过,而不是检查函数是否为 AsyncFunction?
我们无法可靠地检测函数是否返回承诺,以及我们是否需要事先将回调传递给它。 ( Function.length
或解析Function.toString()
还不够好)这就是asyncify
作用——将返回 promise 的函数转换为接受回调的函数。 因为我们可以检测async
函数,因为它们具有不同的类型,我们可以自动asyncify
它们(这是内部工作的方式)。 但是对于碰巧返回promise的普通函数,在Async调用它们之前我们无从得知。
@aearly是的,所以也许更简单的事情比如检测顶级回调?
async function parallel(o, cb) {
if (!cb) { // promise behavior
return Object.fromEntries(
await Promise.all(Object.entries(o).map(async ([k, v]) => [k, await v()]))
)
}
// else do the callback behavior
}
我们已经这样做了——如果您省略对async.parallel
的回调,则返回一个承诺。 如果您省略回调,我们也不假设您传递给它的函数会返回承诺,因为我们无法假设用户意图。 也许我误解了你的意思?
好的,让我们具体一点: https: //repl.it/@caub/async -async
// npm.im/async expects all inner functions to be async,
// even when using no outer callback (as 2nd arg of async.parallel)
console.log(
await async.parallel({
foo: async () => delay(1000).then(() => 1),
bar: async (rs) => {
return 2
},
})
);
// Doesn't work without those async,
// but I'd like this to resolve just like above instead of freezing
console.log(
await async.parallel({
foo: () => delay(1000).then(() => 1),
bar: () => {
return 2
},
})
);
我认为,在未来的下一个主要版本中,仅以类似于以下的方式支持回调可能会很有趣:
await Promise.all([
{ then(resolve){ setTimeout(resolve, 50, 1) } }, // callback way
2,
delay(45).then(())=>3)
])
// [1, 2, 3]
消除内部函数中不必要的async
关键字的需要
优点是更清晰。 我理解你关于不假设用户意图的观点,但你仍然假设人们在想要使用 Promise 解析时不会忘记async
关键字
这会以相当大的方式破坏向后兼容性——例如,你不能再做像Async.map(fileNames, fs.readFile)
这样的事情了。
你能解释一下它会在哪里破裂吗?
目前的突破是:
await async.map(['server.js', 'package.json'], fs.readFile) // works
await async.map(['server.js', 'package.json'], fs.promises.readFile) // works
await async.map(['server.js', 'package.json'], s => fs.promises.readFile(s)) // doesn't work
await async.map(['server.js', 'package.json'], async s => fs.promises.readFile(s)) // works
这个想法是修复第三个
我经常遇到这些问题,例如我写道:
// ...
doc = await async.map(items, item => item.toDoc(currentUser, options));
某处,再次超时,因为我错过了async item => item.toDoc..
如果我一个人在这种情况下,这是不值得的,但如果更多人处于这种情况,我认为值得考虑提议的改变
这个带有async.map
例子也很有代表性,因为你可以看到我的观点
doc = await Promise.all(items.map(item => item.toDoc(currentUser, options)));
无需async item => item.toDoc..
没有办法可靠地修复第三个,这在过去已经讨论了很多。 我们不能检查函数的返回值,然后追溯不向函数传递回调。 async
关键字非常有用。
@caub无耻插件在这里,但我认为asyncp
有能力处理你在这里寻找的东西。
即使这个问题已经关闭,对于上面链接的问题,我想在这里再次强调@caub声明。
当前的行为让我很困惑,虽然我(有点)理解这里讨论的技术限制,但我相信文档根本不符合当前的行为。
例如 mapLimit 它说“返回:一个承诺,如果没有通过回调” - 我从回调版本开始,然后基本上忽略了回调并且很惊讶我没有得到承诺(因为我没有'async' 关键字)。
此外,此时不需要 iteratee 函数中的“回调”语句,而是需要“返回”一个值。
因此,无论是行为应该改变(首选)还是至少文档和示例应该与我的观点保持一致以避免混淆。
@caub这就是原因
https://github.com/caolan/async/blob/8aecf108b3922bc5211036706a0f6f75e02bd42b/lib/internal/wrapAsync.js#L3 :L5
function isAsync(fn) {
return fn[Symbol.toStringTag] === 'AsyncFunction';
}
这样,在不实际调用它的情况下,更容易检查函数是否返回了一个 promise。 但仍然同意你的看法,这违反了 async/await 约定。 在大多数情况下,添加没有内部await
的async
关键字实际上被认为是一种不好的做法。
来自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
async
函数的主体可以被认为是由零个或多个 await 表达式分割的。 顶级代码,直到并包括第一个 await 表达式(如果有),都是同步运行的。
...因此它将是无用的,最糟糕的是,如果代码由 babel 转译,它会增加一些性能开销,因为它会因为async
关键字的存在而添加更多代码和步骤
例如,以下内容:
async function foo() { return 1 }
...相当于:
function foo() { return Promise.resolve(1) }
但是有了这个图书馆,
async.mapLimit([1], 1, async v => v)
和
async.mapLimit([1], 1, v => Promise.resolve(v)))
不等价!
最有用的评论
即使这个问题已经关闭,对于上面链接的问题,我想在这里再次强调@caub声明。
当前的行为让我很困惑,虽然我(有点)理解这里讨论的技术限制,但我相信文档根本不符合当前的行为。
例如 mapLimit 它说“返回:一个承诺,如果没有通过回调” - 我从回调版本开始,然后基本上忽略了回调并且很惊讶我没有得到承诺(因为我没有'async' 关键字)。
此外,此时不需要 iteratee 函数中的“回调”语句,而是需要“返回”一个值。
因此,无论是行为应该改变(首选)还是至少文档和示例应该与我的观点保持一致以避免混淆。