无法捕获Promise队列错误

时间:2016-09-22 13:10:35

标签: node.js promise queue spawn

我正在设置一个节点应用程序,我必须按顺序运行一系列异步生成任务,所以我已经建立了一个队列系统。它在没有错误时运行良好。但是,它没有捕获错误并在它们发生时记录它们。这就是我现在的位置:

function queue(tasks) {
    let index = 0;
    const runTask = (arg) => {
        if (index >= tasks.length) {
            return Promise.resolve(arg);
        }
        return new Promise((resolve, reject) => {
            tasks[index++](arg).then(arg => resolve(runTask(arg))).catch(reject);
        });
    }
    return runTask();
}

function customSpawn(command, args) {
    return () => new Promise((resolve, reject) => {
        const child = spawn(command, args, {windowsVerbatimArguments: true});
        child.on('close', code => {
            if (code === 0) {
                resolve();
            } else {
                reject();
            }
        });
    });
}

队列的构建和执行方式如下:

myqueue.push(customSpawn('cmd.exe', ['/c', convertpath, filelist[i], '-interlace', 'line', '-chop', croppixels, '-resize', '300', outfile]));
queue(myqueue).then(([cmd, args]) => {
        console.log(cmd + ' finished - all finished');
    }).catch(function(error) {
            console.error(error.stack);
            });

抛出以下错误: Uncaught (in promise) TypeError: undefined is not a function(…)

1 个答案:

答案 0 :(得分:1)

抱歉,我无法理解您的queue功能,因此我使用Array.prototype.reduce重构了它。这是以紧凑的方式链接事物的标准方法。以下模块为您的案例建模并将在节点中运行。它演示了错误处理的工作原理。如果出现错误,我假设您要中止整个链?

'use strict';
const insp = require('util').inspect;

const size = 10;
const throwAt = 6;

var template = [1,2,3,4,5,6,7,8,9,];
var cmds = template.map((_, i) => ({cmd: `cmd ${_}`, args: [`arg1-${i}`, `arg2-${i}`]}));
var queue = [];

// promise factory
function makePromise (command, args) {
  return () => new Promise((resolve, reject) => {
    setTimeout(_ => {if(command.indexOf(throwAt) > 0) {
      return reject(command);  // whatever is passed here will hit the catch
    }
    console.log(`${command}\t${insp(args)}`);
    resolve()
    }, Math.random() * 1000)
  })
}

// populate the queue
cmds.forEach(c => queue.push(makePromise(c.cmd, c.args)));
// then execute it and catch all errors
queue.reduce((q, p) => q.then(p), Promise.resolve())
  .catch(e => console.log(`error: ${e}`));

你也可以像这样添加一些重试逻辑......

// promise factory
function makePromise (command, args) {
  return () => new Promise((resolve, reject) => {
    setTimeout(_ => {
      if(command.indexOf(throwAt) > 0 && command.indexOf('retry') === -1) {
        return makePromise(command + 'retry', args)()
          .then(_ => resolve(), e => reject(e));
      }
      console.log(`${command}\t${insp(args)}`);
      resolve()
    }, Math.random() * 1000)
  })
}

错误传播

玩弄这个,我注意到在resolvereject回调中抛出的任何错误都会导致调用调用块的reject,将错误对象传递为单一的论点。这意味着可以抛出错误,而不仅仅是调用reject。这也有提供堆栈跟踪的好处。

管理级联

通过向链中的每个promise添加reject回调,可以根据需要管理错误传播。但是,如果添加了reject calback,则必须在需要传播时重新抛出错误。

以下是这两个原则的实现......

function makePromise2 (command, args) {
  return (retry) => new Promise((resolve, reject) => {
    if(retry){
      console.log(`throw at ${command}`);
      throw new Error(`sorry, tried twice!`);
    }
    setTimeout(_ => {
      if(command.indexOf(throwAt) > 0) {
/*
        if(retry)   // throwing here will not be handled
          throw new Error(`sorry, tried my best!`);
*/
        return makePromise2(command, args)(true)
          .then(resolve, reject);  // without this it will fail silently
      }
      console.log(`${command}\t${insp(args)}`);
      resolve();
    }, Math.random() * 1000)
  })
}

function Reject (cmd) {
  return function reject (e) {
  console.log(`re-throw at ${cmd.cmd}`);
  throw e;  // if you have a reject callback then you must propagate the error
}}

// populate the queue
cmds.forEach(c => queue.push(makePromise2(c.cmd, c.args)));
// then execute it and catch all errors
// the Reject gives the opportunity to manage the error cascade
queue.reduce((q, p, i) => q.then(p, Reject(cmds[i])), Promise.resolve())
  .catch(e => console.log(`catch...\n${e.stack}`));