How to convert callback sample to deferred object?

2019-06-19 07:03发布

问题:

I have a function that accepts a callback function where I pass the data back in. Can this converted to a deferred object for better practice?

Here is what I got:

    var chapters;
    var getChapters = function (fnLoad) {
        //CACHE DATA IF APPLICABLE
        if (!chapters) {
            //CALL JSON DATA VIA AJAX
            $.getJSON('/chapters.txt')
                .done(function (json) {
                    //STORE DATA IN LOCAL STORAGE
                    chapters = Lawnchair(function () {
                        this.save(json, function (data) {
                            //CALL CALLBACK ON DATA
                            fnLoad(data);
                        });
                    });
                });
        } else {
            //RETURN ALREADY CREATED LOCAL STORAGE
            chapters.all(function (data) {
                //CALL CALLBACK ON DATA
                fnLoad(data);
            });
        }
    };

Then I simply use it like this:

this.getChapters(function (data) {
    console.log(data);
});

How can I use it like a promise though while maintaining the cache approach?

this.getChapters().done(function (data) {
    console.log(data);
});

回答1:

var chapters;
var getChapters = function (fnLoad) {
    var d = new $.Deferred();
    //CACHE DATA IF APPLICABLE
    if (!chapters) {
        //CALL JSON DATA VIA AJAX
        $.getJSON('/chapters.txt')
            .done(function (json) {
                //STORE DATA IN LOCAL STORAGE
                chapters = Lawnchair(function () {
                    this.save(json, function (data) {
                        //CALL CALLBACK ON DATA
                        d.resolve(data);
                    });
                });
            })
            .fail(function() { d.reject(); });
    } else {
        //RETURN ALREADY CREATED LOCAL STORAGE
        chapters.all(function (data) {
            //CALL CALLBACK ON DATA
            d.resolve(data);
        });
    }
    return d.promise();
};

Relevant example



回答2:

I see you have already accepted an answer, however if you take a large mental leap and store a promise of chapters instead of the chapters themselves, then the code will simplify significantly.

These days, this is probably the more generally adopted approach for a "fetch/cache" situation.

var chapters_promise;
var getChapters = function () {
    //Cache data if applicable and return promise of data
    if (!chapters_promise)
        chapters_promise = $.getJSON('/chapters.txt').then(Lawnchair).then(this.save);
    return chapters_promise;
};

What is actually promised (the chapters) will be determined by the value(s) returned by the functions Lawnchair and this.save, so you still have some work to do.

getChapters() will always return a promise, regardless of whether the data needs to be fetched or is already cached. Therefore, getChapters() can only be used with promise methods .then(), .done(), .fail() or .always(), for example :

getChapters().then(fnLoad);

You have no other way to access the chapters but that is reasonable since at any call of getChapters(), you don't know whether it will follow the $.getJSON() branch or the simple return branch, both of which return an identical promise.