将同步代码重新格式化为异步

时间:2017-01-04 17:54:20

标签: javascript asynchronous mongoose callback

我有一个同步遍历数组的方法,然后将数据结构返回给调用者。从逻辑上讲,该函数的功能如下:

const myFunction = (inputarray) => {
  let returnarray = [];
  for (let element of inputarray){
    let returnelement = {
      a: element.A,
      b: element.B
    }
    if (returnelement.b == 5){
      returnelement.c = a + b;
    }
    returnarray.push(returnelement);
  }
  return returnarray;
}

到目前为止一切顺利。但是,我意识到要填充returnelement.c,我需要从我的数据库中获取一些东西。我使用带有mongoose的MongoDB数据库。因此,我将使用element.a获取我放入c的内容。这意味着我需要进行异步调用以获取此数据。

const myFunction = (inputarray) => {
  let returnarray = [];
  for (let element of inputarray){
    let returnelement = {
      a: element.A,
      b: element.B
    }
    if (returnelement.b == 5){
      MyType.find({ key: returnelement.b }, (err, value) => {
        returnelement.c = value;
      });
    }
    returnarray.push(returnelement);
  }
  return returnarray;
}

这显然不会奏效。我的函数不会等待异步调用完成,并且在完成循环后才会返回。

我希望函数在从数据库获取所有值时将完成的结构返回给调用者。我想这意味着我需要重构我的方法以使其同步,并在完成后调用回调,但我无法看到一个干净的方法来执行此操作。我无法在mongooses回调中调用回调,因为我无法确定回调是最后一个完成的回调。我甚至不知道for循环中有多少次迭代甚至可以进行数据库调用。 (returnelement.b == 5)。

一种想法是创建一个以数组长度开头的变量,并为每个解析的回调扣除1,如果returnelement.b != 5扣除1,并且当该值在回调中达到0时,我们调用函数的回调并传递returnarray。我希望有比这更好,更干净的版本?

3 个答案:

答案 0 :(得分:3)

  

我希望有比这更好,更干净的版本?

Promises.创建一个promises数组,每个元素一个,然后等待它们解析。既然你已经拥有了一个数组,那么很容易将它映射到一个promises数组并将其传递给Promise.all

const myFunction = (inputarray) => {
  return Promise.all(inputarray.map(element => new Promise((resolve, reject) => {
    let returnelement = {
      a: element.A,
      b: element.B
    }
    if (returnelement.b == 5){
      MyType.find({ key: returnelement.b }, (err, value) => {
        returnelement.c = value;
        resolve(returnelement);
        // handle error?
        // reject(err);
      });
    } else {
       resolve(returnelement);
    }
  })));
}

myFunction([...])
  .then(resultArray => console.log(resultArray))
  .catch(error => console.log(error)))

当然myFunction现在会返回一个承诺,所以调用myFunction必须能够处理它。

正如@Tomalak指出的那样,mongoose本身实际上会返回promises,因此我们可以将其简化为:

const myFunction = (inputarray) => {
  return Promise.all(inputarray.map(element => {
    let returnelement = {
      a: element.A,
      b: element.B
    }
    if (returnelement.b == 5){
      return MyType.find({ key: returnelement.b }).then(value => {
        returnelement.c = value;
        return returnelement;
      });
    }
    return Promise.resolve(returnelement);
  }));
}

答案 1 :(得分:0)

您可以使用SynJS编写和执行同步代码:

function myFunction(inputarray) {
    var returnarray = [];
    for (var i=0; i<inputarray.length; i++){
        var element = inputarray[i];
        var returnelement = {
            a: element.A,
            b: element.B
        };
        if (returnelement.b == 5){
            MyType.find({ key: returnelement.b }, function(err, value){
                returnelement.c = value;
                SynJS.resume(_synjsContext); //<-- indicates that callback is finished
            });
            SynJS.wait(); //<-- waits for callback to finish
        }
        returnarray.push(returnelement);
    }
    return returnarray;
}

然后你可以调用这样的函数:

SynJS.run(myFunction, null, function (ret) {
    console.log('result:', ret);
});

答案 2 :(得分:0)

async和await是你的朋友。你可以使用Promise感知生成器做一些事情,但我们会坚持使用更简单的生成器。

function findData(key){

  return Promise.resolve(MyType.find({ key }));
}

async function myFunction(inputarray){
  let returnarray = [];
  for (let element of inputarray){
    let returnelement = {
      a: element.A,
      b: element.B
    }
    if (returnelement.b == 5){
      returnelement.c = await findData(returnelement.b);
    }
    returnarray.push(returnelement);
  }
  return returnarray;
}

顶部的樱桃会使用像asynquence这样的图书馆。