V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
t123yh
V2EX  ›  JavaScript

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

  •  
  •   t123yh · 2017-05-31 23:18:17 +08:00 · 5929 次点击
    这是一个创建于 2493 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

    使用 Node.js 7.10.0 运行。

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

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

    20 条回复    2017-06-02 14:22:31 +08:00
    seki
        1
    seki  
       2017-05-31 23:34:29 +08:00
    seki
        2
    seki  
       2017-05-31 23:38:59 +08:00
    你这个例子不能直接浏览器运行,所以只能说我的感觉,你第一种写法应该算是直接 resolve 了这个返回的 promise
    zzuieliyaoli
        3
    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
        4
    t123yh  
    OP
       2017-05-31 23:54:21 +08:00 via Android
    @seki 貌似没有看到 async function 主动返回 Promise 的情形。
    t123yh
        5
    t123yh  
    OP
       2017-05-31 23:56:55 +08:00 via Android
    @zzuieliyaoli 我是想要这样的效果:用户在 回答了 promptText 过后,立即弹出下一个 prompt,同时 setTimrout 在后台运行,不阻塞 prompt 的过程。在我电脑( Nodejs 7.10.0 )上,Style 1 会导致下一个 prompt 在上一个 prompt 执行完之后才弹出。
    zzuieliyaoli
        6
    zzuieliyaoli  
       2017-06-01 00:02:35 +08:00
    不要用 setTimeout 后台轮训了,不优雅。
    改写一下 Promise,主动触发 resolve,类似于 jQuery 的 Deffered 对象。

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

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

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

    async function run(){return 1}

    run().then(...).catch(...)
    SoloCompany
        15
    SoloCompany  
       2017-06-01 13:00:12 +08:00 via iPhone   ❤️ 1
    @t123yh 之前的回复已经告诉过你了不可能,因为 es 的语法糖实现定义,await 会对 promise 无限展开,永远不可能返回 promise,你只能把 promise 包一层来避免展开
    SoloCompany
        16
    SoloCompany  
       2017-06-01 13:23:45 +08:00 via iPhone
    @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
        17
    iamdhj  
       2017-06-01 14:27:46 +08:00
    把两个异步操作分开处理就好了,那样逻辑还清晰一点
    joesonw
        18
    joesonw  
       2017-06-01 17:49:39 +08:00
    ```
    const futures = tasks.map(processTask(task));
    await Promise.all(futures);
    ```
    joesonw
        19
    joesonw  
       2017-06-01 17:50:34 +08:00
    Reply 18
    joesonw 几秒前
    ```
    const futures = tasks.map(processTask);
    await Promise.all(futures);
    ```
    Icemic
        20
    Icemic  
       2017-06-02 14:22:31 +08:00
    这都是什么啊,async 函数本来不就返回 Promise 么?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1553 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 43ms · UTC 17:13 · PVG 01:13 · LAX 10:13 · JFK 13:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.