【发布时间】:2016-01-10 08:20:32
【问题描述】:
我的情况与这篇博文中描述的类似:Polling with promises。作者描述了在返回 JobID 之前如何使用 promise 进行轮询。我想使用Q 转换它。
我很乐意发布代码作为起点,但我不确定要发布什么。理想情况下,我试图将承诺链接在一起。我一直在尝试 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 格式。我的设计实现是使用 Promise 来:
(1)从客户端上传文件到服务器(Node);
(2)使用ZamZar API启动图片转换(获取JobID);
(3) 使用 JobID,轮询 ZamZar API 以获取状态更新,直到图像可供下载。图像准备好后,我可以获取 fileId 并将文件下载回 Node 服务器。
(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');
});
});
【问题讨论】:
-
Here is the author's code代码有缺陷,所以不要将其用作起点 -
谢谢@Jaromanda,有什么特别突出的缺陷吗?
-
1 - 重试永远不会反馈到投票中,因此
retry将始终未定义,并且投票的第一行将始终将其设置为 5,因此这将永远重试。 2 - 即使重试被反馈到 poll 中,poll 中的第一行会在达到 0 时将其重置为 5,因此下一行永远不会为真 -
术语不清楚。在引用的
poll函数中,没有说明导致延迟调用的data.state的字符串值。你能告诉我们它可能是什么吗?您如何设想startImageProcessingJob目标函数会遇到这种状态。 -
@Traktor53,我已经用额外的 cmets 更新了这个问题。我不确定 data.state 的价值是什么。 ZamZar API 将返回 JSON 字符串作为响应。根据 API 调用,响应将包含 JobId(用于处理文件转换)或 FileId(当文件准备好下载时)。
标签: javascript promise q