如何使用reduce正确地链接诺言

时间:2018-12-10 15:10:14

标签: javascript node.js promise dropbox

我有一个系统想要使用api在保管箱中创建许多文件夹,但是我似乎试图立即创建所有文件夹,这会导致产生错误,说明我从保管箱执行了太多写操作。

我的代码如下,首先使用reduce来创建多个我认为已链接的promise。在这些承诺中,调用了add函数,该函数将案例上传到mongodb,然后为其创建一个保管箱文件夹,但这会导致节流错误。

bulkAdd: function (req, callback) {
  issues = []
  i = 1

  req.reduce((promise, audit) => {
    return promise.then(_ => this.add(audit, function(err,data){
      if (err){
        console.log('\n'+i+ ' ' + data.scanner_ui + '\n');
      }
    }));
  }, Promise.resolve()).catch(error => {console.log(error)});
},

add: function (req, callback) {
  delete req.status
  var audit = new Audit(req);
  if (req['status_value'] != undefined && req.status_value != ''){
    console.log(req['status_value'])
    audit.status = [{
      status_value : req['status_value'],
      status_notes : req['status_notes'],
      status_date : req['status_date'],
    }]
  }

  audit.save(function (err, data) {
    if (err) {
      callback(err, data)
    }
    else {
        return dropbox_functions.createFolder(data.ui)
          .then(response => {console.log(response)}, error=> {console.log('\n\n\n',error.error.error)})
        .catch(error => console.log(error))
    }
  }); 

},

3 个答案:

答案 0 :(得分:2)

因此,您当前问题中的问题出在您的add函数没有返回值的事实上,我只看到它返回未定义的值。

如果一个承诺在其then / catch块中返回了一个承诺以外的东西,它将使用此输入执行以下功能,并且它将不等待任何内部进程运行之前完成

如果在您的then / catch块中,您将返回一个承诺,它将等待继续下一个then块,从而顺序处理请求。

现在,在您当前的代码中,我认为最简单的方法是处理reduce内的解析,因为您似乎已经被回调句柄卡住了。

req.reduce((promise, audit) => {
  return promise.then(_ => new Promise( 
    function( resolve, reject) {
      this.add(audit, function(err,data){
        if (err){
          console.log('\n'+i+ ' ' + data.scanner_ui + '\n');
          reject( err );
          return;
        }
        resolve( data );
      });
    })
  );
}, Promise.resolve()).catch(error => {console.log(error)});

在这种情况下,承诺本身可能是rejectresolve。我选择分别提交errdata,以便最终在发生错误的情况下进行处理,以便您可以成功保存最后保存的数据

答案 1 :(得分:0)

要正确链接,应确保返回一个Promise对象。

您的reduce最终会创建类似这样的承诺链。

Promise.resolve()
    .then(() => {
        console.log('setting up first timeout');
        setTimeout(() => {
            console.log("1 wait");
        }, 2000);
    })
    .then(() => {
      console.log('setting up second timeout');
        setTimeout(() => {
            console.log("2 wait");
        }, 2000);
    })
    .catch(err => console.log("error", err));

如果您运行它,您会发现它在向下移动链之前不会等待一个承诺结束。

在第二个示例中,它等待第一个示例完成,因为第一个Promise返回了Promise对象。

Promise.resolve()
  .then(() => {
    return new Promise(function(resolve, reject) {
      console.log('setting up first timeout');
      setTimeout(() => {
        console.log("1 wait");
        resolve();
      }, 2000);
    });
  })
  .then(() => {
    return new Promise(function(resolve, reject) {
      console.log('setting up second timeout');
      setTimeout(() => {
        console.log("2 wait");
        resolve();
      }, 2000);
    });
  })
  .catch(err => console.log("error", err));

区别在于第一个示例不返回Promise对象。因此,如果您确保每个成功处理程序都返回一个Promise对象,而该对象仅在您的add函数执行完后才能解决,那么您就可以了。

答案 2 :(得分:0)

除非有充分的理由将bulkAdd()编写为nodeback风格,否则您将发现返回承诺更加方便。对nodeback的需求将消失,.reduce()将更舒适地位于函数内部。

使用.reduce()并保留nodeback样式是可能的,但很麻烦,因为它涉及从nodeback到promise以及从诺言回到nodeback的相当丑陋的双重改组。

假设您可以随意调整呼叫者以接受返回的promise,则代码将如下所示:

'bulkAdd': function(req) {
    return req.reduce((promise, audit) => {
        return promise.then(_ => this.add(audit));
    }, Promise.resolve());
},
'add': function(req) {
    delete req.status;
    var audit = new Audit(req);
    if (req.status_value != undefined && req.status_value != '') {
        audit.status = [{
            'status_value': req.status_value,
            'status_notes': req.status_notes,
            'status_date': req.status_date
        }];
    }
    return new Promise((resolve, reject) => { // in-line promisification of audit.save()
        audit.save((err, data) => {
            err ? reject(err) : resolve(data);
        });
    })
    .then(data => dropbox_functions.createFolder(data.ui));
},

所有捕获和日志记录均已故意删除

细节可能有所不同,主要取决于:

  • 您希望将什么数据(如果有)传递给呼叫者
  • 发生错误时想要发生的事情。