承诺是不可改变的,它们的保证价值意味着什么?

时间:2017-03-05 00:07:39

标签: javascript callback ecmascript-6 es6-promise

我试图了解differences between es6 promises and regular 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));

3 个答案:

答案 0 :(得分:2)

承诺是单向锁定。一旦用一个值解决它或者用一个理由拒绝它,它的状态和价值/原因永远不会改变。所以,无论你对同一承诺做多少次.then(),你总会得到相同的结果。这是"不可变的"装置

我不确定保证价值的含义。无法保证承诺永远不会得到解决。它可能会拒绝(因此没有值),或者如果操作永远不会完成,它可能永远不会解决或拒绝。

操作类型的示例是为异步操作设计的,例如Ajax调用或从文件中读取一些字节。该操作是异步的(在操作开始后,解释器的正常执行继续)并且操作具有特定的开始和结束。在大多数情况下,操作可以成功完成,在这种情况下,它可以有一个值,或者它可能以错误结束,在这种情况下它有错误。值和错误都可以是对象,因此如果结果超过简单值,它们可以具有许多属性。

例如,Ajax调用具有特定的开始和结束。它不能不止一次结束,所以它是承诺的完美匹配。您将获得一个承诺,表示ajax操作的最终结果。然后注册一个履行处理程序和拒绝处理程序,并在操作完成时调用一个或另一个。

普通回调只是回调,每次调用它们时都可以给它们一个不同的值,并且可以多次调用它们。

如果您希望在某些操作完成并且操作具有特定的开始和结束时只获得一次通知,请使用承诺。

如果您想多次收到通知,请使用普通回调或事件监听器或观察者或其他一些可以多次触发的机制。

作为一个简单的例子,setTimeout()非常适用于承诺。

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

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

或者,使用Bluebird Promise库循环浏览URL列表,下载内容,解析内容,在内容中查找某些特定URL然后全部收集它们(也称为抓取) :

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);
});

并且,setInterval()根本没有任何承诺,因为它希望每次时间间隔过去都会反复通知您,而这将无法使用承诺。坚持使用setInterval()的回调。

答案 1 :(得分:2)

为了与标准回调系统进行比较,让我们创建一个可以生成这样的通知对象的类。它将具有与Promise类似的接口,但它实现了一个简单的回调系统:

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);
    }
}; 

因此,与Promise一样,您可以向该类的构造函数传递一个函数来执行某些工作并在适当的时间指示该值。您还可以将侦听器附加到它,该值将在值可用时调用。

这最后一个短语强调了一个重要的事实:如果该值可用,但您还没有附加监听器(回调),即使您在事实之后附加了监听器,也会错过该通知。

以下是基于回调的代码,您可以将其与您在文章中引用的代码进行比较:

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 :(得分:-1)

当promise变量被解析时,在调用它时解析的值返回。 若要使用多个,您必须按如下方式调用它。

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));