无法捕获 Promise 队列错误

can't catch Promise queue errors

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

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(…)

抱歉,我不太理解您的 queue 功能,所以我使用 Array.prototype.reduce 重构了它。这是一种以紧凑方式链接事物的标准方法。以下模块对您的案例进行建模,并将 运行 in node.它演示了错误处理的工作原理。我假设你想在出现错误时中止整个链?

'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。这也有提供堆栈跟踪的好处。

管理级联

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

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

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}`));