在什么情况下可以解决承诺而不是火灾回调?

时间:2014-09-15 17:10:16

标签: javascript jquery promise jquery-deferred deferred

Chrome console

这里我已经分配了这个承诺,我正在记录一个临时的全局变量temp1。如您所见,当我输出temp1的状态时,它返回resolved。然而,当我将done处理程序附加到temp1时,它永远不会触发。

temp1是指$.Deferred,但我也尝试使用temp1.promise().done(),但无济于事。

我做错了什么?

pv5.widgets = new function () {
    /**
     * @var array _widget_queue A map of widget IDs to promises that are resolved
     *     when they are loaded
     */
    this._widget_queue = [];

    /**
     * @var array _js_queue A map of JavaScript dependency names to promises that
     *     are resolved when they are loaded
     */
    this._js_queue = [];

    /**
     * Loads the HTML into the widget's container
     * @param object w The widget implementation in pv5.widgets
     * @param object widget The widget's configuration data
     * @return jQueryPromise A promise that is resolved when the HTML loads
     */
    this._get_res = function (w, widget) {
        var r = widget.config.res || widget.res;
        if (!r) {
            return $.Deferred().resolve().promise();
        }
        typeof r != 'string' && (r = r[pv5.user.auth.role] ? r[pv5.user.auth.role] : 'blank.html');
        var path = (widget.product_level || widget.config.product_level ? pv5.widgets._APP_WIDGET_RESOURCE_ROOT : pv5.widgets._WIDGET_RESOURCE_ROOT) + r;
        return $.get(path, {_v: pv5._NOCACHE ? new Date().getTime() : ''})
            .done(function (html) {
                w._owner.html(html + '<div style="clear: both;"></div>');
            })
            .fail(function () {
                console && console.error('Failed to load resource for widget ' + id + '.', arguments);
            });
    };

    /**
     * Loads the provided widget and all resources it requires
     * @param string id The identifier for the widget
     * @param object widgets The widget library map
     * @param boolean only_required Only load if it is required
     * @return jQueryPromise A promise that resolves when the widget is loaded
     */
    this.loadWidget = function (id, widgets, only_required) {
        // First, make sure we haven't already started loading it; if we have,
        // return the existing promise
        var widget_loading = $.inArray(id, $.map(this._widget_queue, function (o) { return o.widget; }));
        if (widget_loading > -1) {
            return this._widget_queue[widget_loading].promise;
        }

        // Look in the provided widgets, or merge the registry and activeApp
        // widgets if none were provided
        widgets = widgets || $.extend({}, pv5.widgets._REGISTRY, pv5.activeApp._WIDGETS, true);

        var widget = widgets[id];

        // Make sure the widget exists
        if (!widget) {
            console && console.error('Widget ' + id + ' is not registered in widget library.');
            return $.Deferred().reject().promise();
        }

        // Configure it if it isn't already
        if (!widget.configured) {
            var configured = $.extend({
                _id: id,
                required: false,
                configured: true,
                auth: true,
                config: {
                    required: false,
                    res: false,
                    auth: false
                }
            }, pv5.widgets._REGISTRY[id]);
            $.extend(configured.config, widgets[id]);

            configured._version = pv5._VERSION + '_' + (configured.version || 0) + '_' + (configured.config.version || 0);
            widgets[id] = widget = configured;
        }

        // Don't load the widget if only_required was passed in and it's not required
        // (only_required is used in loadWidgets for page load)
        if (only_required && !widget.required && !widget.config.required) {
            return $.Deferred().resolve().promise();
        }

        // Load JavaScript dependencies
        if (widget.js || widget.config.js) {
            var j = widget.config.js || widget.js;
            // Normalize to an array of JavaScript files
            typeof j == 'string' && (j = [j]);
            j = j || [];
            // Create a queue (array of objects with promises)
            // For each one, include a full path to the file we need
            // The path property is checked to ensure we don't push the same script
            // on the queue twice, and finished is just for debugging convenience
            // started is checked to see if the script has begun loading
            // properties for making debugging easier
            // promise is resolved when the script is loaded and it proceeds to the
            // next script in the queue
            $.map(j, function (script) {
                var path = (widget.product_level || widget.config.product_level ? pv5.widgets._APP_WIDGET_JS_ROOT : pv5.widgets._WIDGET_JS_ROOT) + script;
                if ($.inArray(path, $.map(pv5.widgets._js_queue, function (o) { return o.path; })) < 0) {
                    pv5.widgets._js_queue.push({
                        path: path,
                        promise: $.Deferred(),
                        started: false,
                        finished: false
                    });
                }
            });

            var load_script = function (index) {
                if (index > pv5.widgets._js_queue.length - 1) {
                    return;
                }

                var script = pv5.widgets._js_queue[index];
                if (!script.started) { // If it hasn't already initiated
                    script.started = true;
                    $.getScript(script.path).done(function () {
                        script.finished = true;
                        script.promise.resolve();
                    });
                }

                script.promise
                    .done(function () {
                        load_script(index + 1);
                    })
                    .fail(function () {
                        console && console.error('Failed to load script ' + script.path + '.', arguments);
                    });
            };
            load_script(0);
        }

        // Similar structure for the widget queue
        // Queue item is resolved when the widget, all dependencies, and resources are done
        this._widget_queue.push({
            widget: id,
            promise: $.Deferred(),
            started: true,
            finished: false
        });

        // Get widget source file
        var lib;
        if (widget.config.src || widget.src) {
            var path = (widget.product_level || widget.config.product_level ? this._APP_WIDGET_ROOT : this._WIDGET_ROOT) + (widget.config.src || widget.src);
            lib = $.get(path, {_v: widget._version + (pv5._NOCACHE ? '_' + new Date().getTime() : '')})
        } else {
            lib = $.Deferred().resolve().promise();
        }

        // The queue item we created above will resolve when all resources are loaded
        // We will return it
        var widget_loaded = this._widget_queue[this._widget_queue.length - 1].promise;

        // Load the source file before its resources
        lib
            .done(function () {
                // When it's loaded, identify the DOM element (_owner) and also
                // add it to the public widgets registry and start to populate it
                var w = pv5.widgets[id] = pv5.widgets[id] || {};
                w._owner = $(pv5.widgets._WIDGET_DOM_BIND_PREFIX + id).addClass(pv5.user.auth.role);
                w._config = widget.config;

                // Global events
                $.each(['onStart', 'onSetMode', 'onAuthChange'], function (i, event) {
                    widget[event] && $(document).on('pv5.' + event, $.proxy(widget[event], w));
                    w[event] && $(document).on('pv5.' + event, $.proxy(w[event], w));
                });
                // Widget events
                $.each(['onHide', 'onShow'], function (i, event) {
                    widget[event] && w._owner.on('pv5.' + event, $.proxy(widget[event], w));
                    w[event] && w._owner.on('pv5.' + event, $.proxy(w[event], w));
                });

                // sync_widgets - Bind another widget to show and hide when this widget is toggled
                var sync = widget.config.sync_widgets || widget.sync_widgets;
                if (sync && sync.length) {
                    $.each(sync, function (i, widget) {
                        w._owner
                            .on('pv5.onShow', function () { $(pv5.widgets[widget]).show(); })
                            .on('pv5.onHide', function () { $(pv5.widgets[widget]).hide(); });
                    });
                }

                // Check user roles and show or hide content in this widget based on privilege
                pv5.widgets._handle_auth(w, widget);

                // Create a translate method that runs through the widget's container and
                // looks for content that can be replaced with localized content
                w.translate = function () {
                    var _this = this;
                    $('[data-lang]', _this._owner).each(function () {
                        var key = $(this).attr('data-lang');
                        if (key in _this.lang) {
                            $(this).html(_this.lang[key]);
                        }
                    });
                    _this.onTranslate && _this.onTranslate();
                };

                // Get the HTML that is dumped into the container
                var res = pv5.widgets._get_res(w, widget);

                // Get the CSS files and drop them into the <head>
                var css = (function () {
                    var c = widget.config.css || widget.css;
                    typeof c == 'string' && (c = [c]);
                    c = c || [];
                    $.each(c, function (i, stylesheet) {
                        var path = (widget.product_level || widget.config.product_level ? pv5.widgets._APP_WIDGET_CSS_ROOT : pv5.widgets._WIDGET_CSS_ROOT) + stylesheet;
                        // Make sure the stylesheet isn't a duplicate
                        if ($('link[rel="stylesheet"]').filter(function () { return $(this).attr('href').split('?')[0] == path; }).length) {
                            return true; // continue
                        }
                        $('<link>').attr({rel: 'stylesheet', href: path + '?_v=' + (pv5._NOCACHE ? '_' + new Date().getTime() : '')}).appendTo($('head'));
                    });
                    return $.Deferred().resolve().promise();
                })();

                // Load the templates and place them in w.templates object
                var templates = (function () {
                    var t = widget.config.templates || widget.templates;
                    typeof t == 'string' && (t = [t]);
                    t = t || [];
                    if (!t.length) {
                        return $.Deferred().resolve().promise();
                    }
                    w.templates = {};
                    return $.when.apply($, $.map(t, function (template) {
                        var path = (widget.product_level || widget.config.product_level ? pv5.widgets._APP_WIDGET_TEMPLATE_ROOT : pv5.widgets._WIDGET_TEMPLATE_ROOT) + template;
                        return $.get(path, {_v: pv5._NOCACHE ? '_' + new Date().getTime() : ''})
                            .done(function (html) {
                                w.templates[template] = html;
                            })
                            .fail(function () {
                                console && console.error('Failed to load widget ' + id + ' template ' + template + '.', arguments);
                            });
                    }));
                })();

                // Load the localized file and drop it into w.lang object
                var lang = (function () {
                    var l = widget.config.has_lang || widget.has_lang;
                    if (!l) {
                        return $.Deferred().resolve().promise();
                    }
                    var promise = $.Deferred();
                    var path = (widget.product_level || widget.config.product_level ? pv5.widgets._APP_WIDGET_LANG_ROOT : pv5.widgets._WIDGET_LANG_ROOT) + id + '/' + (pv5.user.lang || 'en') + '.json';
                    $.get(path, {_v: pv5._NOCACHE ? '_' + new Date().getTime() : ''})
                        .done(function (lang) {
                            var done = function () {
                                $('html').attr('lang', pv5.user.lang || 'en');
                                w.translate();
                            };
                            w.lang = lang;
                            // Merge the platform-level localization with product-level localization
                            // if such a thing exists -- this can be used to selectively override global
                            // language parameters with product-specific ones, e.g. "Welcome" to
                            // "Welcome to CFAexcel"
                            if (widget.merge_product_lang || widget.config.merge_product_lang) {
                                var path = pv5.widgets._APP_WIDGET_LANG_ROOT + id + '/' + (pv5.user.lang || 'en') + '.json';
                                $.get(path, {_v: pv5._NOCACHE ? '_' + new Date().getTime() : ''})
                                    .done(function (lang) {
                                        w.lang = $.extend({}, w.lang, lang);
                                        done();
                                    })
                                    .fail(function () {
                                        console && console.error('Failed to load widget ' + id + ' platform-level language file ' + (pv5.user.lang || 'en') + '.', arguments);
                                        done();
                                    });
                            } else {
                                done();
                            }
                            promise.resolve();
                        })
                        .fail(function () {
                            w.lang = {};
                            console && console.error('Failed to load widget ' + id + ' language file ' + (pv5.user.lang || 'en') + '.', arguments);
                            promise.resolve();
                        });
                    return promise;
                })();

                // Recover the item in the queue that refers to this widget
                var queue_item = pv5.widgets._widget_queue[$.inArray(id, $.map(pv5.widgets._widget_queue, function (o) { return o.widget; }))];

                // Resolve and init when HTML, CSS, templates, language, and all JavaScript dependencies have loaded
                $.when.apply($, [res, css, templates, lang].concat($.map(pv5.widgets._js_queue, function (o) { return o.promise; }))).done(function () {
                    queue_item.promise.resolve();
                    queue_item.finished = true;
                    w.init && w.init(w._config);
                });
            })
            .fail(function () {
                console && console.error('Failed to load widget ' + id + ' lib at ' + path + '.', arguments);
            });

        return widget_loaded;
    };
};

当已经加载了给定的窗口小部件时,它是从pv5.widgets.loadWidget(widget_name)返回的承诺。

1 个答案:

答案 0 :(得分:1)

直接回答您的问题:

var d = $.Deferred().reject(); // create an empty rejected promise
d.state = function(){ return "resolved"; }; // override `state`

d.promise().done(function(){
   // without any more quirky stuff - this will never run.
})

除了那些和类似的肮脏技巧,pretty much never