我遇到了与此博文中描述的类似情况:Polling with promises .作者描述了如何使用 promise 进行轮询,直到返回 JobID。我想使用 Q 转换它.
我很乐意发布代码作为起点,但我不确定要发布什么。理想情况下,我正在尝试将 promise 链接在一起。我一直在试验 Q.delay() 但它似乎没有实现我的目标。
var promise = uploadFile();
promise.then(startImageProcessingJob)
.then(saveConvertedImage);
能否请您提供有关如何创建将继续轮询直到检索到数据(或达到最大尝试次数)的 promise 的建议。
这是作者使用 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 Canvas 中(使用 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');
});
});
最佳答案
可能感兴趣的设计思路:
- 为使用
returnObj
实现的 promise 编写一个 onFulfill 监听器 (pollZamZarForOurPngFile
)。 - 此监听器返回一个 Promise 对象。
- 如果 zambar 已完成转换,返回的 promise 将通过
returnObj
实现(将其传递到链中)。 - 如果发生 zamzar 错误或重试次数过多,则返回的 promise 将被拒绝。
请注意,这会将文件检索留给 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;
}
关于javascript - 使用 Q 进行 promises 轮询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34689020/