What does it mean for promises to be immutable and their guaranteed value?

感情迁移 提交于 2020-06-28 04:41:32

问题


I'm trying to understand the differences between es6 promises and regular callbacks but don't get the examples below. Can someone show what it would look like to do the below with callbacks?

// an immediately resolved promise
var p2 = Promise.resolve("foo"); 

// can get it after the fact, unlike events
p2.then((res) => console.log(res)); 

var p = new Promise(function(resolve, reject) {  
   setTimeout(() => resolve(4), 2000);
});

// handler can't change promise, just value
p.then((res) => {  
  res += 2;  
  console.log(res);
});

// still gets 4
p.then((res) => console.log(res));

回答1:


To make the comparison with a standard callback system, let's create a class that can produce such notifier objects. It will have an interface much like Promise has, but which implements a simple callback system:

class Notifier {
    constructor(executor) {
        // The object will maintain a list of callbacks
        this.callbacks = [];
        // The executor is executed now and will allow the client
        // to determine the logic of this notifier object:
        // ...when the callbacks need to be called and with which value:
        executor( (res) => {
            // The client can call this resolve function to indicate
            // the value. So now the callbacks need to be called with it:
            this.callbacks.forEach(callback => callback(res));
        });
    }
    addListener(callback) {
        // This method resembles the `then` of promises: it allows
        // a client to pass a callback function, that should be called
        // when the value becomes available (i.e. when the event triggers).
        this.callbacks.push(callback);
    }
}; 

So, like with Promise, you can pass to the constructor of this class a function to do some work and indicate the value at the appropriate time. You can also attach listeners to it, which will be called at the moment the value becomes available.

This last phrase highlights an important fact: if the value becomes available, but you did not attach a listener (callback) yet, you'll miss the notification, even if you attach the listener after the facts.

Here is the callback-based code which you could compare with the code you quoted from the article:

class Notifier {
    constructor(executor) {
        // The object will maintain a list of callbacks
        this.callbacks = [];
        // The executor is executed now and will allow the client
        // to determine the logic of this notifier object:
        // ...when the callbacks need to be called and with which value:
        executor( (res) => {
            // The client can call this resolve function to indicate
            // the value. So now the callbacks need to be called with it:
            this.callbacks.forEach(callback => callback(res));
        });
    }
    addListener(callback) {
        // This method resembles the `then` of promises: it allows
        // a client to pass a callback function, that should be called
        // when the value becomes available (i.e. when the event triggers).
        this.callbacks.push(callback);
    }
}; 

// a notifier that immediately notifies the result
f2 = new Notifier( (resolve) => resolve("foo") );
// but since no-one was listening, no callback is called.

// canNOT get it after the fact, unlike promises
f2.addListener((res) => console.log(res));
// ... nothing gets called or printed: we are too late.

// 
var f = new Notifier(function(resolve) {  
   setTimeout(() => resolve({ data: 4}), 2000);
});

// handler CAN change the outcome
f.addListener((res) => {  
  res.data += 2;
  console.log(res.data);
});

// ... now also this one gets 6!
f.addListener((res) => console.log(res.data));



回答2:


A promise is a one-way latch. Once it is resolved with a value or rejected with a reason, its state and value/reason can never change. So, no matter how many times you do .then() on the same promise, you will always get the same result. That's what "immutable" means.

I'm not sure what you mean by a guaranteed value. There is no guarantee that a promise will ever resolve. It might reject (and thus not have a value) or it might never resolve or reject if the operation just never completes.

An example of the type of operation promises are designed for is an asynchronous operations such as an Ajax call or reading some bytes from a file. The operation is asynchronous (normal execution of the interpreter continues after the operation was started) and the operation has a specific start and end to it. In most case, the operation may complete successfully in which case it can have a value or it may end with an error in which case it has an error. Both value and error can be objects so they can have many properties if the result is more than a simple value.

An Ajax call, for example has a specific start and end. It can't end more than once so it is a perfect match for promises. You get a promise that signifies the eventual result of the ajax operation. You then register both a fulfill handler and a reject handler and one or the other will be called when the operation has completed.

Plain callbacks are just callbacks and they can be given a different value every time they are called and they can be called more than once.

If you want to get notified once and only once when some operation completes and the operation has a specific begin and end, use a promise.

If you want to get notified more than once, use a plain callback or an event listener or an observer or some other mechanism that can be trigger more than once.


As a simple example, setTimeout() works very well with a promise.

function delay(t) {
    return new Promise((resolve, reject) => {
        resolve();
    }, t);
}

// delay 100ms before starting the operation
delay(100).then(run);

Or, a little more involved operation using the Bluebird Promise library to cycle through a list of URLs, download the content, parse the content, look in the content for some specific URLs and then collect them all (otherwise known as scraping):

const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'), {multiArgs: true});
const cheerio = require('cheerio');

function getAsync() {
    return request.getAsync.apply(request, arguments).then(argArray => {
        // return only the response html
        if (argArray[0].statusCode !== 200) {
            throw new Error("response statusCode = " + argArray[0].statusCode);
        }
        return argArray[1];
    });
}

const urls = [....];
Promise.mapSeries(urls, url => {
     return getAsync({url: url, gzip: true}).then(html => {
         let $ = cheerio.load(html);
         let resources = $("#external_resources_list li a.filename");
         resources.each(index, link) => {
             let href = $(link).attr("href");
             console.log(href);
             results.push(href);
         });
     }).catch(err => {
         // log error, but keep going
         console.log(url, err);
     });
}).then(() => {
    // got all results here
    console.log(results);
});

And, setInterval() does not work at all with a promise because it wants to notify you repeatedly everytime the time interval passes and that will simply not work with promises. Stick with a callback for setInterval().




回答3:


when the promise variable is resolved, the value resolved when it is recalled returns. to use more than one, you must call it as follows.

var p = new Promise(function(resolve, reject) {  
   setTimeout(() => resolve(4), 2000);
});

p.then((res) => {  
  res += 2;  
  console.log(res);
  return res
})
.then((res) => console.log(res));


来源:https://stackoverflow.com/questions/42602868/what-does-it-mean-for-promises-to-be-immutable-and-their-guaranteed-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!