我有类似于此博客文章中所述的情况:Polling with promises。作者描述了在返回JobID之前如何使用promises进行轮询。我想使用Q转换它。
我很乐意将代码作为起点发布,但我不确定要发布什么。理想情况下,我试图将承诺链接在一起。我一直在试验Q.delay(),但它似乎没有实现我的目标。
var promise = uploadFile();
promise.then(startImageProcessingJob)
.then(saveConvertedImage);
是否可以提供有关如何创建承诺的建议,该承诺将继续轮询,直到检索到数据(或满足最大尝试次数)。
以下是使用bluebird的作者代码。
var getJobStatusAsync = Promise.promisifyAll(api);
function poll(jobId, retry) {
if(!retry) retry = 5;
if(!retry--) throw new Error('Too many retries');
return getJobStatusAsync(jobId)
.then(function(data) {
if(data.state === 'error') throw new Error(data.error);
if(data.state === 'finished') return data;
return Promise.delay(jobId, 10000).then(poll);
});
在回应Traktor53的评论时,我正在添加到目前为止的逻辑。我试图避免添加导致问题膨胀的额外代码。
在我的Angular应用程序中,我想使用ZamZar第三方服务将图像转换为PNG格式。我的设计实现是使用promises:
(1)将文件从客户端上传到服务器(Node);
(2)使用ZamZar API开始图像转换(获取JobID);
(3)使用JobID,轮询ZamZar API进行状态更新,直到映像准备下载。图像准备就绪后,我可以获取fileId并将文件下载回节点服务器。
(4)一旦PNG图像重新出现在我的服务器上,我想将图像返回到客户端浏览器并放入HTML画布(使用three.js和fabric.js)。
/* Dependencies */
var express = require('express');
var request = require('request');
var formidable = require('formidable');
var randomstring = require("randomstring");
var fs = require('fs');
var Q = require('q');
/*
* Helper functions in Node
*/
convertFileUtil = function() {
/**
* Step 1: upload file from client to node server.
* formidable is used for file upload management. This means the file is
* automatically uploaded to a temp directory. We are going to move the
* uploaded file to our own temp directory for processing.
* Return Type: A Promise is returned with payload containing the directory
* path for the image file. This path is referenced in subsequent chained methods.
*/
var uploadFileFromClientToNodeServer = function(req) {
var q = Q.defer();
var form = new formidable.IncomingForm();
var tmpFolder = 'upload/' + randomstring.generate() + '/';
//Use formidable to parse the file upload.
form.parse(req, function(err, fields, files) {
if (err) {
console.log(err);
throw err;
}
//When upload is successful, create a temp directory and MOVE file there.
//Again, file is already uploaded. There is no need to use fs.writeFile* methods.
mkdirp(tmpFolder, function (err) {
if (err) {
q.reject(err);
} else {
//File will be saved here.
var tmpFileSavedLocation = tmpFolder + files.file.name;
//Call fs.rename to MOVE file from formidable temp directory to our temp directory.
fs.rename(files.file.path, tmpFileSavedLocation, function (err) {
if (err) {
q.reject(err);
}
console.log('File saved to directory:', tmpFileSavedLocation);
q.resolve(tmpFileSavedLocation);
});
}
});
});
return q.promise;
};
/**
* Step 2: Post the temp file to zam zar. ZamZar is an API service that converts
* images to a different file format. For example, when a user uploads an Adobe
* Illustrator EPS file; the file is sent to zamzar for conversion to a PNG. all
* image formats are returned as PNG which will be added to the canvas.
* Return: This promise will return the JobID of our submission. The JobID will be
* used in subsequent promise to retrieve the converted image.
*/
var postTempFileToZamZar = function(filePath) {
console.log('FilePath', filePath);
var q = Q.defer();
var formData = {
target_format: 'png',
source_file: fs.createReadStream(filePath),
};
//console.log('OK', formData);
//Send file to zamzar for conversion.
request.post({ url: 'https://sandbox.zamzar.com/v1/jobs/', formData: formData }, function (err, response, body) {
if (err) {
console.log('An error occurred', err);
q.reject(err);
} else {
var jsonData = JSON.parse(body);
console.log('SUCCESS! Conversion job started:', jsonData.id);
//This object will be returned in promise payload.
var returnObj = {
filePath: filePath,
jobId: jsonData.id,
};
console.log('Process complete. Returning: ', returnObj);
q.resolve(returnObj);
return q.promise;
}
}).auth(zamzarApiKey, '', true);
};
/*
* Step 3: Poll for PNG.
*/
var pollZamZarForOurPngFile = function(dataObj) {
console.log('pollZamZarForOurPngFile', dataObj);
}
//API
return {
uploadFileFromClientToNodeServer: uploadFileFromClientToNodeServer,
postTempFileToZamZar: postTempFileToZamZar,
pollZamZarForOurPngFile: pollZamZarForOurPngFile,
};
};
//Call to convert file.
app.post('/convertFile', function (req, res) {
var util = convertFileUtil();
//Get file data.
var promise = util.uploadFileFromClientToNodeServer(req);
promise
.then(util.postTempFileToZamZar)
.then(util.pollZamZarForOurPngFile);
.then(function(data) {
console.log('Done processing');
});
});
答案 0 :(得分:2)
使用标准Promise / A +
的poll
函数的工作版本
function poll(jobId, retry) {
if(!retry) retry = 5; // default retries = 5
function delay(timeout) {
return new Promise(function(fulfill) {
setTimeout(function() {
fulfill();
}, timeout);
});
}
function poller() {
if(!retry--) throw new Error('Too many retries');
return getJobStatusAsync(jobId)
.then(function(data) {
if (data.state === 'error') throw new Error(data.error);
if (data.state === 'finished') return data;
return delay(10000).then(poller);
});
}
return poller();
};
我觉得这段代码可能写得太远了#34;更好" ......但是星期六,所以这是"周末代码",它至少为OP提供了更好的跳跃点
答案 1 :(得分:2)
可能感兴趣的设计理念:
pollZamZarForOurPngFile
的承诺编写一个onFulfill监听器(returnObj
)。 returnObj
(将其传递给链)来实现。请注意,这会将文件检索到promise链中的下一个(onFulfilled)侦听器。我使用Promise是为了方便,因为node.js支持它并且它的Promise / Aplus兼容。根据需要将其转换为Q. 轮询请求代码是直接关闭zamzar网站,可能来自教程示例 - 请检查它。
function pollZamZarForOurPngFile( returnObj)
{ var jobID = returnObj.jobId;
var resolve, reject;
function unwrap( r, j) { resolve = r, reject = j};
var promise = new Promise( unwrap);
var maxRetry = 5;
var firstDelay = 500; // 1/2 second
var retryDelay = 5000; // 5 second?
function checkIfFinished()
{ // refer to https://developers.zamzar.com/docs under node.js tab for documentation
request.get ('https://sandbox.zamzar.com/v1/jobs/' + jobID,
function (err, response, body)
{ if (err)
{ reject( new Error("checkIfFinished: unable to get job"));
return;
}
if( JSON.parse(body).status == "successful")
{ resolve( returnObj); // fulfill return promise with "returnObj" passed in;
return;
}
// has not succeeded, need to retry
if( maxRetry <= 0)
{ reject( new Error("checkIfFinished: too many retries"));
}
else
{ --maxRetry;
setTimeout(checkIfFinished, retryDelay);
}
}
}).auth(apiKey, '', true);
setTimeout(checkIfFinished, firstDelay);
return promise;
}