javascript - 使用 Q 进行 promises 轮询

标签 javascript promise q

我遇到了与此博文中描述的类似情况: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');
  });
});

最佳答案

可能感兴趣的设计思路:

  1. 为使用 returnObj 实现的 promise 编写一个 onFulfill 监听器 (pollZamZarForOurPngFile)。
  2. 此监听器返回一个 Promise 对象。
  3. 如果 zambar 已完成转换,返回的 promise 将通过 returnObj 实现(将其传递到链中)。
  4. 如果发生 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/

相关文章:

javascript - 硬重载后,vuex 状态未加载到 nuxtjs Mounted() 中

javascript - 我如何检查对象是 div 还是 li?

javascript - 理解 Promise 和 Await

javascript - 在 node.js 中返回一个延迟的嵌套 promise

javascript - MVC AJAX调用url格式

javascript - 为什么我的函数在修改数组时会更改参数?

javascript - 如何读取用户输入的 Node.js - 使用 Promises 重构

javascript - 如何让 Promise 在已经解决时不重新运行代码?

javascript - 在 Node.js 中使用循环和文件读取进行 Promise

node.js - 使用 Nodejs 和 Sequelize 进行 Promise