سؤال

AFAIK Deferreds can only be resolved once, but I have a scenario where I'm storing a lot of Deferreds in a table. It's possible that these deferreds' values will need to be updated but that other modules will want to store a local reference to the original deferred, so I can't just overwrite the entire deferred with a new object.

Is there some way I can overwrite the deferred's value or if (as I suspect) it's inadvisable (even impossible) are there any tried and tested approaches to this problem? Here's some skeleton code to illustrate the problem

// articles module
var articles = {},

// anId = 'uuid';

articles[anId] = $.get('/articles/' + anId);

//another module

var localCopyOfArticle,
    getArticle = function (anId) {
        localCopyOfArticle = articles[anId]
    }
getArticle('uuid');

// back to articles
articles[anId] = $.get('/articles/refresh/' + anId);

//another module again
console.log(localCopyOfArticle) // oh no - it's out of date
هل كانت مفيدة؟

المحلول

AFAIK Deferreds can only be resolved once

Correct. A promise that transitioned from pending state to fulfilled or rejected states (aka was resolved) can no longer change state. The promise specification says this explicitly:

When fulfilled/rejected, a promise must not transition to any other state.

So, there you have it, no - promises do not change states after being resolved. Just like a function that returned a value doesn't suddenly throw an exception in retrospect. Whenever you're unsure - think about the sync code analogy

var x = foo(); // returns correctly
// .. more code

You wouldn't expect foo to throw after it returned or return after it threw just like you wouldn't expect a promise to reject after it resolved or resolve after it rejected.


Is there some way I can overwrite the deferred's value or if (as I suspect) it's inadvisable (even impossible) are there any tried and tested approaches to this problem? Here's some skeleton code to illustrate the problem

No, a promise is an abstration over a single computation. Settling it twice makes no sense. It seems like it is the wrong abstraction for your goal.

Promises are a great flow control mechanism but they're not intended to solve every problem asnychronosu code has to deal with. They are not event emitters for one.

This problem however can be solved with promises rather easily:

// articles module
var articles = {}, 

// anId = 'uuid';

articles.get = function(anId){
    if(articles[anId]){
        return $.when(articles[anId]); // return cached copy
    }
    return $.get('/articles/' + anId).then(function(article){
        articles[anId] = article;
        return article;
    });
} 
articles.refresh = function(anId){
     return $.get('/articles/refresh/' + anId).then(function(article){
          articles[anId] = article;
          return article;
     });
});

//another module

var localCopyOfArticle,
    getArticle = function (anId) {
        return localCopyOfArticle = articles.get(anId); // now a promise
    }


// back to articles
// the .then here is __CRUCIAL__ since the refresh operation is async itself
articles.refresh(anId).then(function(article){ 
    console.log(article); // fresh
    return getArticle(anId)
}).then(function(article){
    console.log(article); // from getArticle, also fresh!
});

This code has a lot of 'cruft' to illustrate the point. The important thing is to realize that the operations are a new operation each time. Fetching the data is a new operation each time and not the same one. Just like in synchronous code. Store the values in the map and not the promises and the fetch operation should be promise based.

نصائح أخرى

rather than storing the deferred in the articles module you could store a jquery Callbacks object and trigger it when the ajax call completes. This will allow you to have multiple subscribers and allow you to notify each subscriber multiple times:

// articles module
var articles = {},

// anId = 'uuid';

// create jquery callbacks object.
articles[anId] = $.Callbacks();

// load the article and fire all callbacks that have been added to articles[anId]
$.get('/articles/' + anId)
    .then(function (data) { articles[anId].fire(data); });

// add a callback to articles[anId] whose purpose is to keep localCopyOfArticle up to date.
var localCopyOfArticle = null;
articles[anId].add(function(data) {
    localCopyOfArticle = data;
});

// back to articles
articles[anId] = $.get('/articles/refresh/' + anId)
    .then(function (data) { articles[anId].fire(data); });
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top