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

如果你自认熟悉 Promise,来猜一下这个代码的运行结果

  •  
  •   autoxbc · 2019-07-24 05:13:32 +08:00 · 5720 次点击
    这是一个创建于 1709 天前的主题,其中的信息可能已经有所发展或是发生改变。
    const onResolved = e => console.log('resolve , ', e );
    const onRejected = e => console.log('reject , ', e );
    
    new Promise( ( resolve , reject ) => {
    	resolve( new Promise( ( resolve , reject ) => {
    		resolve(42);
    	} ) );
    } ).then( onResolved , onRejected );
    
    new Promise( ( resolve , reject ) => {
    	resolve( new Promise( ( resolve , reject ) => {
    		reject(42);
    	} ) );
    } ).then( onResolved , onRejected );
    
    new Promise( ( resolve , reject ) => {
    	reject( new Promise( ( resolve , reject ) => {
    		resolve(42);
    	} ) );
    } ).then( onResolved , onRejected );
    
    new Promise( ( resolve , reject ) => {
    	reject( new Promise( ( resolve , reject ) => {
    		reject(42);
    	} ) );
    } ).then( onResolved , onRejected );
    

    如果猜错了,说明之前看的 Promise 教程都不合格,我还没见过合格的

    第 1 条附言  ·  2019-07-25 01:44:16 +08:00

    继续关注这个帖子的朋友,送上一道附加题

    const onResolved = e => console.log(e);
    
    new Promise( ( resolve , reject ) => {
    	resolve( new Promise( ( resolve , reject ) => {
    		resolve(42);
    	} ) );
    } ).then(onResolved);
    
    new Promise( ( resolve , reject ) => {
    	resolve( Promise.resolve(43) );
    } ).then(onResolved);
    
    Promise.resolve( new Promise( ( resolve , reject ) => {
    	resolve(44);
    } ) ).then(onResolved);
    
    Promise.resolve( Promise.resolve(45) ).then(onResolved);
    
    33 条回复    2019-08-01 13:42:10 +08:00
    jiangzhuo
        1
    jiangzhuo  
       2019-07-24 05:54:39 +08:00
    这有点难度……我觉得就算教程合格了,也不一定能答对,至少需要个草稿纸……

    应该很多人挨个运行能答出来,比如
    resolve , 42
    reject , 42
    reject , 一个 Promise
    reject,一个 Promise 并且里面那个的 reject 没有 handle

    我觉得能答成这样就算及格了。真要把四个 promise 一起,你还得给人家其他条件才能算出正确结果了,意义不大……
    binux
        2
    binux  
       2019-07-24 06:12:49 +08:00
    这个是大家来找茬吗?
    autoxbc
        3
    autoxbc  
    OP
       2019-07-24 06:18:00 +08:00
    @jiangzhuo #1 一个一个答对也算合格。不过我看过的教程,都没有清晰的解释 new Promise() 的行为,使得很多人说不出四段代码的关键区别
    qdwang
        4
    qdwang  
       2019-07-24 07:40:10 +08:00 via iPhone
    还真从来没在实际中用过 reject 一个 promise。能说说哪种情况下需要这么写吗
    zjsxwc
        5
    zjsxwc  
       2019-07-24 07:56:31 +08:00 via Android
    这个是最基本都 promise 用法啊,
    楼主这里相对正常使用时,奇葩一点的是被 resolve 和 reject 了一个 promise,
    当然是在 resolve 和 reject 之前执行这个 promise 里面的代码呗。
    iceheart
        6
    iceheart  
       2019-07-24 08:08:36 +08:00 via Android
    UnhandledPromiseRejectionWarning:
    ??
    jaskle
        7
    jaskle  
       2019-07-24 08:23:39 +08:00 via Android
    我决定放进 node 跑一下
    jinliming2
        8
    jinliming2  
       2019-07-24 08:25:10 +08:00   ❤️ 11
    promise 的 resolve 是递归的,只要 resolve 一个 promise,就会继续等待这个 promise。
    而 reject 碰到就直接返回,如果 reject 的是一个 promise,这个 promise 不会被等待。
    所以:
    第一个 外层 promise 里 resolve 了一个 ( resolve 的 promise ),所以 then 的结果会递归等待里面的 promise 返回,里面的 promise resolve 了一个 42,所以得到了 “ resolve 的 42 ”。
    第二个 外层 promise 里 resolve 了一个 ( reject 的 promise ),所以 then 的结果会递归等待里面的 promise 返回,里面的 promise reject 了一个 42,碰到 reject 直接返回,被 then 的第二个参数抓到,所以得到了 “ reject 的 42 ”。
    第三个和第四个 外层 promise 直接 reject 了一个 promise (不管这个 promise 具体是啥),所以直接返回,被 then 的第二个参数抓到,所以得到 “ reject 的 promise<xxx>”。
    其中第三个因为内层 promise 是 resolve 的 42,所以结果是 “ reject 的 promise<resolve 42>”。
    而第四个因为内层 promise 是 reject 的 42,外层 promise 被 then 的第二个参数抓住了,但内层的没有,所以结果是 “ reject 的 promise<reject 42> 并带有一个未捕获的来自内部 promise 的异常”。

    不管你这个 promise 写多少层,resolve 都会递归下去,一旦碰到 reject 立即返回。
    jinliming2
        9
    jinliming2  
       2019-07-24 08:32:02 +08:00   ❤️ 1
    所以:
    const a = async () => {
    xxxxx;
    return promise;
    };
    await a();
    如果 xxxxx 里没用到 await 语句,最后 return 了一个 promise,那么 a 的 async 标志可以去掉,变成一个普通函数。因为加上 async 标志,相当于是 resolve 了两次,而去掉就相当于变成 resolve 一次。(最后调用的时候依然得 await,像这样:)

    const a = () => {
    xxxxx;
    return promise;
    };
    await a();
    Sparetire
        10
    Sparetire  
       2019-07-24 08:37:06 +08:00 via Android
    关键是 reject 不会递归地 resolve 里面的 Promise 吧,猜测那就应该是先 reject 了后面两个并打印了两个 pending 的 Promise,然后再是 resolve 42 和 reject 42
    zqx
        11
    zqx  
       2019-07-24 08:42:25 +08:00 via Android
    如果楼主没见过合格的教程,你怎么知道你的结果就是唯一正确的呢?
    Snail233
        12
    Snail233  
       2019-07-24 09:11:31 +08:00
    马克下,后面看
    temporary
        13
    temporary  
       2019-07-24 09:31:29 +08:00
    mcfog
        14
    mcfog  
       2019-07-24 09:44:04 +08:00 via Android
    这种考察是否 promise 熟悉的方式我认为不合格
    nikandaoleshenme
        15
    nikandaoleshenme  
       2019-07-24 09:47:01 +08:00
    额,表示这样的代码看都不想看,难道是真香定律 [狗头]
    daishankeke
        16
    daishankeke  
       2019-07-24 09:53:10 +08:00   ❤️ 1
    reject,promise 对象( resolved )
    reject,promise 对象( rejected )
    resolve,42
    reject,42

    我的理解:

    有一个 Promise 对象我们称他为 p1
    p1 在执行 resolve 时有一个参数 result,reject 执行时有一个参数 reason
    先来看 resolve:
    p1 的 resolve 方法被执行时,如果发现 result 是一个 Promise 对象就会这样处理:
    result.then(resolve, reject)
    其实是将自己的 resolve、reject 方法移交给 result,等待 result 去 resolve 之后,才会执行 p1 的 resolve,然后 p1 在 resolve 时才会执行 p1.then 里边的 onFulfilled 和 onReject 方法。
    再看 reject 的情况
    reject 方法不会检测 reject 时传入的 reason 是否是一个 Promise 对象,而是直接开启一个微任务清空 reject 的回调队列,同时将返回值 reason 传入,由于没有将自己的 reject 和 resolve 方法移交给 reason 这一步,所以在这题中他们要比上面的两个 resolve(new Promise)快。

    简单说就是 resolve 会判断传进来的 result 参数是不是一个 Promise,是的话会移交自己的 resolve 和 reject 方法,reject 则不会。

    当然... 如果你想清晰的了解具体是怎么执行的,我建议去看看那些符合 PromiseA+规范的 Promise 实现

    😂这两天我正在看 Promise,如果有不对的请指出
    caocong
        17
    caocong  
       2019-07-24 10:00:38 +08:00
    Promise 内没有 resolve 是种错误的写法 会编译报错 就如上面第二个和第四个
    这里主要是 resolve 的问题 并不是直觉的返回了一个 promise 对象
    文档里说的很清楚了 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve

    The Promise.resolve() method returns a Promise object that is resolved with a given value. If the value is a promise, that promise is returned; if the value is a thenable (i.e. has a "then" method), the returned promise will "follow" that thenable, adopting its eventual state; otherwise the returned promise will be fulfilled with the value. This function flattens nested layers of promise-like objects (e.g. a promise that resolves to a promise that resolves to something) into a single layer.

    所以看啥教程 直接读文档不就好了
    mcfog
        18
    mcfog  
       2019-07-24 10:00:57 +08:00 via Android   ❤️ 3
    @jinliming2 你可以仔细看下 https://promisesaplus.com/ 这里有几个要点:1. new Promise 的行为并没有被标准化,2. promise 和 thenable 的处理是不一样的 3. 实践中一个 promise 实现却很难探测出 promise 和 thenable 的区别 4. 标准中的 onRejected 是有递归展开行为的

    补充我自己的回复,楼主的出题基本聚焦于不在标准中的,而且实战中属于需要避免的写法,所以不是一个好的题目,类似 i++ + ++i
    hoyixi
        19
    hoyixi  
       2019-07-24 10:07:02 +08:00
    其实很简单,记得早就说过了,Promise/await/async 这些都是 js 对异步回调的语法糖,根本不是啥高深玩意,你越往深里想,越是把简单的事情想复杂。

    Promise 的 resolve,一定会逐层 resolve,为啥,就好比你 callback 里又有 callback,不 callback 到最后,根本不知道结果;

    Promise 的 reject,直接返回 reject 的值, 为啥,就好比你最外层 callback 直接错误,还往里层走个屁。


    别钻牛角尖,扣学术字眼。就那么简单。就一个语法糖,写起来、维护起来舒服而已,漫天的教程,都在扯淡。
    beginor
        20
    beginor  
       2019-07-24 10:13:14 +08:00 via Android
    既然有了 promise,还是配合 async/await 食用, 疗效更佳
    PyCode
        21
    PyCode  
       2019-07-24 11:22:44 +08:00
    所以有没有合格的教程?
    banricho
        22
    banricho  
       2019-07-24 11:31:00 +08:00
    这……无论是面试题还是业务代码都是不合格的 = =
    都什么年代了。。。
    will0404
        23
    will0404  
       2019-07-24 11:45:40 +08:00   ❤️ 1
    有了 async/await 再去折腾这个没必要了,即使没有 async/await,没有任何场景会需要这样写代码。
    ochatokori
        24
    ochatokori  
       2019-07-24 12:38:01 +08:00 via Android
    js 考题日常
    为了整人出题…
    libo930920
        25
    libo930920  
       2019-07-24 13:42:39 +08:00
    仔细看过 Promise A+规范的,基本都应该能答出来的吧
    abcdGJJ
        26
    abcdGJJ  
       2019-07-24 16:37:56 +08:00
    @temporary node 10.7 上是一致的 node 10 好像改过 promise
    azh7138m
        27
    azh7138m  
       2019-07-24 18:27:43 +08:00
    @zqx 标准是唯一且正确的,可以看标准。
    教程不过是一个演绎(有的连演绎都算不上),可以选择不看。
    Raymon111111
        28
    Raymon111111  
       2019-07-24 18:51:23 +08:00
    那 js 里
    [ + { = ?
    ] + } 呢?
    zqx
        29
    zqx  
       2019-07-24 19:00:54 +08:00 via Android
    @azh7138m 标准也是教程
    azh7138m
        30
    azh7138m  
       2019-07-24 19:44:38 +08:00
    @zqx 那你应该是没看过标准,standard 和 tutorial 可完全不是一个东西
    autoxbc
        31
    autoxbc  
    OP
       2019-07-25 00:12:39 +08:00
    @qdwang #4 这确实不是种模式用法,但也称不上反模式,算是知识盲点吧
    zqx
        32
    zqx  
       2019-07-25 08:45:04 +08:00 via Android
    @azh7138m 有意思,公开的东西想看就看没什么门槛,好像比别人高级?
    libook
        33
    libook  
       2019-08-01 13:42:10 +08:00
    这个知识很有趣,但对教程的全盘否定有些太偏激了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3022 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 14:41 · PVG 22:41 · LAX 07:41 · JFK 10:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.