2015-07-15 3 views
2

Я пытаюсь повторно использовать данные, возвращенные из обещания здесь. Но проблема заключается в том, что после первого вызова функции 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); 
+0

Почему вы не храните результаты в закрытии? –

+0

Обетование нельзя «повторно использовать». Он успешно (один раз), или он терпит неудачу (один раз), или не сделал этого. Это на самом деле очень хорошо. Используйте вторичный механизм для хранения данных. В любом случае обещание будет использоваться .. не асинхронно (т. Е. Обещание еще не завершено еще во втором вызове), что противоречит фундаментальному характеру обещания. (Как правило, плохо сделать функцию «возможно, асинхронно», поскольку она [выпускает Zalgo] (http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony)). – user2864740

ответ

2

Возможно, у вас есть проблема с синхронизацией. Ваша функция apiCall() является асинхронной. Это означает, что он закончится когда-нибудь позже. Таким образом, каждый раз, когда вы вызываете checkPromise(), все, что вы делаете, это запуск запроса, и он заканчивается через некоторое время. Итак, вы называете это в первый раз и запускаете запрос (который еще не закончен). Затем вызывается ваш следующий вызов checkPromise(), и он делает это if до того, как завершится первый звонок. Таким образом, он еще ничего не обнаружил в кеше.

Ваш код выполняет два запроса параллельно, а не один за другим.

Если вы действительно хотите дождаться, пока первый запрос не будет выполнен до выполнения второго, вы должны будете на самом деле структурировать свой код для этого. Вам нужно будет сделать checkPromise(), чтобы вернуть обещание, поэтому код, использующий его, мог бы быть известен, когда он был фактически выполнен, чтобы выполнить что-то после его завершения.

FYI, я ничего не вижу в вашем коде, что на самом деле связано с повторным использованием обещаний (чего вы не можете сделать, потому что это объекты с одним выстрелом).

Вот одна из возможных реализаций:

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

Добавлен пример измененного кода, который не только позволяет упорядочивать вызовы 'checkPromise()', но также удаляет несколько анти-шаблонов для большей согласованности программ и устранения создания ненужных обещаний. – jfriend00

+0

Второй пример можно упростить. Возвращение обещания по вызову API должно быть доверенным обещанием. Если вы позвоните в Promise.resolve с надежным обещанием, он просто вернет обещание. Это означает, что вы можете опустить проверку типа и просто вернуть 'Promise.resolve (rp);' –

+0

@AdamvandenHoven - я внедрил ваше предложение. – jfriend00

0

несколько проблем с вашим кодом, первый в apiCall, вы делаете обещание АНТ-шаблон (не нужно для этого нового обещания), второй ваш checkPromise делает операцию синхронизации, поэтому он должен либо вернуть обещание или иметь аргумент обратного вызова, поэтому код может быть изменен на:

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

также как jfriend00 сказал, я не уверен, как это как-то с повторным использованием обещаний. – mido

0

Учитывая то, как вы во всем мире хранения результата в 'obj[url]', он бы, вероятно, проще всего сделать

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

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

в основном сделать запрос, если он еще не запущен, а затем приложить слушателя к обещанию, когда результат будет загружен.