ECMAScript 的 async 函数可以返回一个 Promise 作为结果吗?

2017-05-31 23:18:17 +08:00
 t123yh

我想实现这样的一个异步函数:以 async 方式询问一些用户输入,然后执行一个耗时的异步操作,再将这个异步操作以 Promise 形式返回。调用数次这个异步函数,将返回的 Promise 放进一个数组,并等待它们全部执行完成。

如果没有看懂我的描述,可以看看具体代码,大概长这样: https://gist.github.com/t123yh/9ace77dc3acc3809562bbd7574503694

使用 Node.js 7.10.0 运行。

这份代码中,有两个地方标记了 Style 1 和 Style 2。仅有 Style 2 能够正常工作; Style 1 无法实现预期目标。

这是一个 feature 吗?如果是,能够找到文档说明吗?我好像没找到。

5974 次点击
所在节点    JavaScript
20 条回复
seki
2017-05-31 23:34:29 +08:00
seki
2017-05-31 23:38:59 +08:00
你这个例子不能直接浏览器运行,所以只能说我的感觉,你第一种写法应该算是直接 resolve 了这个返回的 promise
zzuieliyaoli
2017-05-31 23:51:16 +08:00
```js
const questionAsync = () => new Promise((resolve) => {
setTimeout(() => {
console.log('promptText')
resolve();
}, 100)
});

const processTask = async () => {
const content = await questionAsync();
const myJob = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('content')
resolve();
}, 100);
});

// Style 1
return myJob;

// Style 2
// return { job: myJob };
};

(async function () {
const tasks = [
{ n: "UpLeft", t: 1926 },
{ n: "DownLeft", t: 0817 },
{ n: "UpRight", t: 1500 },
{ n: "DownRight", t: 586 }
];
let v = [];

for (let task of tasks) {
const taskPromise = await processTask(task);

// Style 1
v.push(taskPromise);

// Style 2
// v.push(taskPromise.job);
}
await Promise.all(v);
console.log("All done.");
})()
```

讲道理,楼主应该把你的逻辑抽象出来。

我这边按照我简化的代码运行,style1、styl2 都是可以的。
t123yh
2017-05-31 23:54:21 +08:00
@seki 貌似没有看到 async function 主动返回 Promise 的情形。
t123yh
2017-05-31 23:56:55 +08:00
@zzuieliyaoli 我是想要这样的效果:用户在 回答了 promptText 过后,立即弹出下一个 prompt,同时 setTimrout 在后台运行,不阻塞 prompt 的过程。在我电脑( Nodejs 7.10.0 )上,Style 1 会导致下一个 prompt 在上一个 prompt 执行完之后才弹出。
zzuieliyaoli
2017-06-01 00:02:35 +08:00
不要用 setTimeout 后台轮训了,不优雅。
改写一下 Promise,主动触发 resolve,类似于 jQuery 的 Deffered 对象。

http://liubin.org/promises-book/#deferred-and-promise
2zH
2017-06-01 02:47:29 +08:00
SoloCompany
2017-06-01 02:49:00 +08:00
你还明白吗?根本不需要!
只要没有 await, async function 都是立刻返回一个 promise 的

node -p 'async function x() { return 1; }; x()'
Promise { 1 }
SoloCompany
2017-06-01 02:52:57 +08:00
而如果使用了 await 关键字,promise 是会被无限展开的

这个语法糖过于甜美,以至于你永远不可能从 await 手里得到一个 promise object !
t123yh
2017-06-01 06:38:03 +08:00
@SoloCompany 我是想要让一个函数 部分地 异步执行,我当然知道没有 await 的时候它返回 Promise,我现在想让它 await 过后返回 Promise。按你说的,这样是不行的。请问在什么地方可以找到关于这个的文档呢? MDN 里貌似没有。
t123yh
2017-06-01 06:39:25 +08:00
@zzuieliyaoli 这个 setTimeout 仅作为模拟任务处理来使用,实际代码中是执行一个下载的操作。
funnyecho
2017-06-01 08:38:10 +08:00
async 的 return 应该跟 Promise.resolve() 是一个道理吧,所以你 style 1 返回的 promise 也被展开了。
codehz
2017-06-01 09:52:54 +08:00
你可以使用类似惰性求值的策略,返回一个零参数函数,该函数返回值为 promise,然后第一次 await 之后再(),就可以优雅的解决问题了
acthtml
2017-06-01 09:57:03 +08:00
可以,函数本身就返回 promise

async function run(){return 1}

run().then(...).catch(...)
SoloCompany
2017-06-01 13:00:12 +08:00
@t123yh 之前的回复已经告诉过你了不可能,因为 es 的语法糖实现定义,await 会对 promise 无限展开,永远不可能返回 promise,你只能把 promise 包一层来避免展开
SoloCompany
2017-06-01 13:23:45 +08:00
@t123yh 这里有一吨的语法糖
可以看一下 https://stackoverflow.com/questions/41128311/async-await-in-a-class-method-called-then

await 的参数其实并不是 promise 而是 thenable 的 duck type,await 本身就是 generator is 的语法糖,yield 语句会无限调用 then 函数展开结果
iamdhj
2017-06-01 14:27:46 +08:00
把两个异步操作分开处理就好了,那样逻辑还清晰一点
joesonw
2017-06-01 17:49:39 +08:00
```
const futures = tasks.map(processTask(task));
await Promise.all(futures);
```
joesonw
2017-06-01 17:50:34 +08:00
Reply 18
joesonw 几秒前
```
const futures = tasks.map(processTask);
await Promise.all(futures);
```
Icemic
2017-06-02 14:22:31 +08:00
这都是什么啊,async 函数本来不就返回 Promise 么?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/365055

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX