如何重用承诺?

How to reuse promises?

我正在尝试重用从 promise 返回的数据。但是,问题是,在第一次调用 checkPromise 函数之后,它立即调用了第二个函数,而第一个函数的承诺没有实现,所以它从来没有 returns 任何数据,因此它从不进入 if 子句。我如何重用承诺?

var Promise = require('bluebird');
var request = Promise.promisify(require("request"));


var url = 'http://www.google.com';
var obj = new Object;

function apiCall(url) {
    return new Promise(function (resolve, reject) {

        request(url).spread(function(response, body) {
            return resolve(body);
        }).catch(function(err) {
            console.error(err);
            return reject(err);
        });

    });
}

function checkPromise(url) {
    if(obj.hasOwnProperty(url)) {   
        var rp = obj[url];
        //do something
    }
    else {
        apiCall(url).then(function(result) {            
            obj[url] = result; 
            //do something
        });
    }
}

checkPromise(url);
checkPromise(url);

您可能遇到了时间问题。您的 apiCall() 函数是异步的。这意味着它会在稍后的某个时间完成。因此,每次调用 checkPromise() 时,您所做的只是启动一个请求,它会在稍后完成。因此,您第一次调用它并启动一个请求(尚未完成)。然后,将调用您对 checkPromise() 的下一次调用,并在第一次调用完成之前进行 if 检查。因此,它尚未在缓存中找到任何内容。

您的代码是运行两个并行请求,而不是一个接一个。

如果您真的想等到第一个请求完成后再执行第二个请求,那么您将不得不实际构建代码来执行此操作。您需要使 checkPromise() return 本身成为一个承诺,以便使用它的代码可以知道它实际完成的时间,以便在完成后执行某些操作。

仅供参考,我在您的代码中没有看到任何与重用承诺相关的内容(这是您不能做的事情,因为它们是一次性对象)。

这是一种可能的实现方式:

var Promise = require('bluebird');
var request = Promise.promisify(require("request"));

var url = 'http://www.google.com';
var obj = {};

function apiCall(url) {
    return request(url).spread(function(response, body) {
        return body;
    });
}

function checkPromise(url) {
    if(obj.hasOwnProperty(url)) {   
        var rp = obj[url];
        //do something
        return Promise.resolve(rp);
    }
    else {
        return apiCall(url).then(function(result) {            
            obj[url] = result; 
            //do something
            return result;
        });
    }
}

checkPromise(url).then(function() {
    checkPromise(url);
});

重大变化:

  1. Return 由 request() 编辑的承诺 return 而不是创建另一个承诺。
  2. 更改 checkPromise() 使其始终 return 承诺是否在缓存中找到该值,以便调用代码始终一致地工作。
  3. 对两个 checkPromise() 调用进行排序,以便第一个可以在第二个执行之前完成。

如果您感兴趣的结果已经加载,则有一种非常不同的方法是实际等待缓存。可以这样做:

var Promise = require('bluebird');
var request = Promise.promisify(require("request"));

var url = 'http://www.google.com';
var obj = {};

function apiCall(url) {
    return request(url).spread(function(response, body) {
        return body;
    });
}

function checkPromise(url) {
    if(obj.hasOwnProperty(url)) {   
        // If it's a promise object in the cache, then loading 
        // If it's a value, then the value is already available
        // Either way, we wrap it in a promise and return that
        return Promise.resolve(obj[url]);
    } else {
        var p = apiCall(url).then(function(result) {
            obj[url] = result; 
            //do something
            return result;
        });
        obj[url] = p;
        return p;
    }
}

checkPromise(url).then(function(result) {
    // use result
});

checkPromise(url).then(function(result) {
    // use result
});

您的代码几乎没有问题,首先在 apiCall 中,您正在执行承诺蚂蚁模式(不需要新的承诺),其次您的 checkPromise 正在执行同步操作,因此它必须 return一个promise或者有一个回调参数,所以你的代码可以改成:

var Promise = require('bluebird');
var request = Promise.promisify(require("request"));


var url = 'http://www.google.com';
var obj = new Object;


function apiCall(url) {

    return request(url).spread(function(response, body) {
        return body;
    }).catch(function(err) {
        console.error(err);
        throw err;
    });
}


function checkPromise(url) {
    var promise = Promise.resolve();
    if(obj.hasOwnProperty(url)) {   
        var rp = obj[url];
        //do something
    }
    else {
        return apiCall(url).then(function(result) {            
            obj[url] = result; 
            //do something
        });
    }

    return promise;

}

checkPromise(url).then(function(){
    return checkPromise(url);
});

考虑到您将结果全局存储在 'obj[url]' 中的方式,这可能是最容易做到的

function checkPromise(url) {
    if (!obj[url]) obj[url] = apiCall(url);

    obj[url].then(function(result) {
        //do something
    });
}

基本上是发出请求,如果它还没有开始,则在加载结果时附加一个侦听器到 promise。

Here 是最简单的示例,说明如果对某物有多个类似请求(例如缓存检查)

,如何防止多次 API 调用
var _cache = {
    state: 0,
    result: undefined,
    getData: function(){
    log('state: ' + this.state);
    if(this.state === 0 ){ // not started
        this.state = 1; // pending
      this.promise = new Promise(function(resolve, reject) {
        return (apiCall().then(data => { _cache.result = data; _cache.state = 2; resolve(_cache.result) }));
      })

      return this.promise;
    }
    else if(this.state === 1){ // pending
        return this.promise;
    }
    else if(this.state === 2){// resolved
        return Promise.resolve(this.result);
    }
  },
};

模拟api通话

function apiCall(){
    return new Promise(function(resolve, reject) {
    log('in promise')

    setTimeout(() => {
        log('promise resolving')
        resolve(1);
    }, 1000);
  })
}

同时发出请求。

_cache.getData().then(result => { log('first call outer: ' + result);
    _cache.getData().then(result => { log('first call inner: ' + result); });
});

_cache.getData().then(result => { log('second call outer: ' + result);
    _cache.getData().then(result => { log('second call inner: ' + result); });
});

只打了一个 API 电话。所有其他人将等待完成或使用已解决的结果(如果已经完成)。