如何重用承诺?

问题描述 投票:4回答:4

我试图在这里重用从promise返回的数据。但是,问题是,在第一次调用checkPromise函数之后,它会立即调用第二个函数,并且第一个函数的承诺未得到满足,因此它永远不会返回任何数据,因此它永远不会进入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);
javascript node.js promise bluebird
4个回答
4
投票

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

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

如果您确实要等到第一个请求完成后再执行第二个请求,那么您必须实际构建代码才能执行此操作。您需要让checkPromise()返回一个promise本身,因此使用它的代码可以知道它何时实际完成以便在完成之后执行某些操作。

仅供参考,我在你的代码中看不到任何与重用promises有关的东西(这是你不能做的,因为它们是一次性对象)。

这是一个可能的实现:

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. 返回request()返回的承诺,而不是创建另一个承诺。
  2. 更改checkPromise(),以便始终返回一个承诺,无论该值是否在缓存中找到,因此调用代码始终可以始终如一地工作。
  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
});

0
投票

你的代码几乎没有问题,首先在apiCall中,你正在做一个promise ant-pattern(不需要那个新的promise),第二个你的checkPromise正在进行同步操作,所以它必须返回一个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);
});

0
投票

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

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

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

基本上发出请求,如果它还没有启动,那么在结果加载时将一个监听器附加到promise。


0
投票

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调用是maden。所有其他人将等待完成或使用已解决的结果(如果已完成)。

© www.soinside.com 2019 - 2024. All rights reserved.