hifive / hifivemain

main repository
http://www.htmlhifive.com/
Other
40 stars 10 forks source link

then 内で reject される Promise を返すと commonFailHandler が実行されない #250

Closed 3bch closed 10 years ago

3bch commented 10 years ago

確認した環境

以下のようなコードを書いたとき result に fail() を設定していないのに commonFailHandler が実行されません。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <script src="lib/jquery/jquery-1.10.2.js"></script>
        <script src="lib/hifive/h5-1.1.8.dev.js"></script>
        <script>

            h5.settings.commonFailHandler = function () {
                console.warn('ここには到達しない');
            };

            var deferred = h5.async.deferred();
            var result = deferred.then(function () {
                var innerDeferred = h5.async.deferred();

                // そのまま rejecct すると即時に innerDeferred に対して commonFailHandler が実行されてしまう
                setTimeout(function () {
                    innerDeferred.reject('err');
                }, 0);

                // done から fail に遷移させる
                return innerDeferred.promise();
            //}).fail(function (e) {
            //  console.warn('ここには到達する')
            });

            deferred.resolve('ok');
        </script>
        <title>Title</title>
    </head>
    <body>
    </body>
</html>
fukudayasuo commented 10 years ago

確認しました。確かにthenのコールバックが返したプロミスについてのcfhは動作しませんでした。 原因は、thenのコールバックがpromiseを返した時にjQuery内部でそのプロミスに対してfail()を呼んでいるためです。

h5.settings.commonFailHandler(function() {
    console.warn('CFH');
});
var d1 = h5.async.deferred();
var d2 = h5.async.deferred();
d1.then(function() {
    return d2.promise();
    // thenの返り値にプロミスを渡すとjQueryがそのプロミスのfail()を呼ぶ
});
d1.resolve();

// jQueryによってd2のfailが呼ばれた状態なので、reject()してもCFHは動作しない
d2.reject();

ユーザコード側でfailコールバックを登録していないのにCommonFailHandlerが呼ばれないのは意図しない動作ですので、対応します。

今は、thenをラップしてその中でjQueryのthenをapply()している実装ですが、その実装である限り回避できない問題と思われますので、thenをFW側で実装します。

pipeも同じ問題があるので、同様に対応します。

fukudayasuo commented 10 years ago

then/pipeの実装を行いましたが、バグがありました。 pipeのコールバックが返したプロミスがreject();されても、pipeの戻り値のプロミスがrejectされていません。

var d = h5.async.deferred();
var a = d.pipe(function() {
    console.log('done');
    return h5.async.deferred().reject().promise()
});
d.resolve();
a.fail(function(){
    // 実行されるべきなのに実行されない。
});
a.state(); // rejectedになるべきなのにpendingになっている。