简体   繁体   English

承诺不会解决

[英]Promise will not resolve

The second part of the Promise below (inside the then ) is never run. 以下Promise的第二部分(在then )永远不会运行。 When I run the database query without using the Promise(in a node script that I run node myscript.js it returns the data but the console never returns the prompt--the console just hangs and I have to send an interrupt manually. Therefore, when I put it inside a Promise, I think the Promise doesn't know that the database query is complete even though it seems to have returned all the data, therefore the second part of the Promise isn't running ( I think). If that's the problem, how do I write the database query so that it doesn't hang and the Promise can run to completion? 在不使用Promise的情况下运行数据库查询时(在运行node myscript.js的节点脚本中,它返回数据,但控制台从不返回提示-控制台只是挂起,我必须手动发送中断。因此,当我将其放入Promise中时,我认为Promise即使已返回了所有数据,也不知道数据库查询是否已完成,因此Promise的第二部分没有运行(我认为)。就是这个问题,如何编写数据库查询,使其不会挂起,并且Promise可以运行完毕?

const sqlite = require('/usr/local/lib/node_modules/sqlite3');
const express = require('/usr/local/lib/node_modules/express')
const promise = require('/usr/local/lib/node_modules/promise')

app.get('/', (request, res) => {

  var res = [];

  function getData() {
    return new Promise(function(resolve, reject) {
      db.each('SELECT column_a, column_b FROM trips group by column_a', (e, rows) => {

        var d = {
          a: rows['column_a'],
          b: rows['column_b']
        }


        res.push(d)
      });
    });

  }
  getData().then(function(data) {
    console.log("never run....", res, data) //never run
  });

})

You need to resolve a promise by calling one of the functions it provides in the callback through its constructor. 您需要通过调用其构造函数在回调中提供的功能之一来解决承诺。

const promise = new Promise((resolve, reject) => {
  // you must call resolve() or reject() here
  // otherwise the promise never resolves
});

Otherwise it will always stay in Pending state and never call the callbacks(s) you pass into then . 否则,它将始终保持待处理状态,并且永远不会调用您传递给then的回调。

promise.then(() => {
  // this never gets called if we don't resolve() or reject()
});

Additionally, promises allow you to resolve with values so there's usually no need to maintain global variables, you can just pass results through. 此外,promise允许您使用值进行解析,因此通常无需维护全局变量,您只需传递结果即可。

Finally, the callback in db.each will be called once for each row , so you would need to handle that by resolving the promise after all rows have been obtained 最后, db.each的回调将为每行调用一次 ,因此您需要通过在获得所有行后解析promise来解决该问题。

Here's how you could write your code: 这是编写代码的方法:

function getData() {
  const data = [];
  return new Promise((resolve, reject) => {
    db.each('SELECT column_a, column_b FROM trips group by column_a', (e, row) => {
      if (e) {
        // error reading a row, reject the Promise immediately
        // optionally you could accumulate errors here in a similar manner to rows
        reject(e); 
        return;
      } 

      // success reading a row, store the row result
      data.push({
        a: row['column_a'],
        b: row['column_b']
      });

    }, (e, rowCount) => { // the complete handler called when the operation is done, see docs: https://github.com/mapbox/node-sqlite3/wiki/API#databaseeachsql-param--callback-complete

      if (e) { 
        // operation finished, there was an error
        reject(e);
        return;
      }

      // operation succeeded, resolve with rows
      resolve(data);
    });
  });
}

app.get('/', (request, res) => {  

  getData().then((data) => {
    // here `data` is an array of row objects
  }, (e) => {
    console.error(`Database error: ${e}`);
  });
});

Side Note 边注

Not sure why you are redeclaring the parameter res as an [] , but there's no need for doing var res = [] . 不知道为什么将参数res重新声明为[] ,但是不需要执行var res = [] Since you already have res , you can just say res = [] to point res to a new array. 由于您已经有了res ,因此您可以说res = []res指向新数组。 Of course that will overwrite the response object so I assume that you're doing it just for the purposes of this example. 当然,这将覆盖响应对象,因此我假设您只是出于本示例的目的而这样做。 If not, you should probably create a new variable. 如果没有,您可能应该创建一个新变量。

You've declared a Promise which means you're responsible for calling one of resolve or reject once and once only . 你已经申报了承诺,这意味着你是负责调用的一个resolvereject 一次,并且只有一次

Here's a cleaned up example: 这是一个清理的示例:

app.get('/', (request, res) => {  
  var res = [ ];

  new Promise((resolve, reject) => {
    db.each('SELECT column_a, column_b FROM trips group by column_a', (e, row) => {
      if (e) {
        reject(e);

        return;
      }

      res.push({
          a: row['column_a'],
          b: row['column_b']
      });
    }, (err) => {
      if (err) {
        return reject(err);
      }

      resolve(res);
    });
  }).then((data) => {
    console.log("Running ", res, data)//never run
  }
});

If your database layer supports promises that usually makes this sort of code a lot less messy since you can simply chain that in there. 如果您的数据库层支持诺言,那么通常可以使这类代码的混乱程度降低很多,因为您可以在其中简单地进行链接。

Edit: Since the Sqlite3 API is bizarrely non-standard and the each function has two callbacks you need to handle each row with the first, then the completion handler with the second. 编辑:由于Sqlite3 API非常不标准,并且each函数都有两个回调,因此您需要使用第一个处理每个row ,然后使用第二个处理完成处理程序。

If you design an API like this you're doing it wrong. 如果您设计这样的API,那么您做错了。 Don't. 别。

Several points : 几点:

  • resolve / reject must be called, otherwise a new Promise() will remain forever "pending". 必须调用resolve / reject ,否则, new Promise()将永远保持“待处理”状态。
  • always promisify at the lowest level possible, ie promisify db.each() not getData() . 始终以最低级别进行承诺,即承诺db.each()而不是getData() This gives you a testable, reusable utility and more comprehensible application code. 这为您提供了一个可测试,可重用的实用程序以及更易于理解的应用程序代码。
  • db.each() is a challenge to promisify because it has two possible sources of error; db.each()是一个挑战,因为它有两个可能的错误源。 one in its iteration callback and one in its complete callback. 一个在其迭代回调中,另一个在其完整回调中。
  • the sqlite3 documentation does not state what happens if an iteration error occurs but presumably the iteration continues, otherwise the error would simply appear as a completion error? sqlite3文档未说明如果发生迭代错误,但大概会继续迭代,会发生什么情况,否则该错误将简单地显示为完成错误?

Here's a couple of ways to promisify : 这有两种实现方式:

1. First iteration error or completion error causes promise rejection - iteration errors are not exposed to your application code. 1.第一次迭代错误或完成错误会导致承诺被拒绝-迭代错误不会暴露给您的应用程序代码。

// Promisification
db.eachAsync = function(sql, iterationCallback) {
    return new Promise(function(resolve, reject) {
        db.each(sql, (iterationError, row) => {
            if(iterationError) {
                reject(iterationError);
            } else {
                iterationCallback(row);
            }
        }, (completionError, n) => {
            if(completionError) {
                reject(completionError);
            } else {
                resolve(n); // the number of retrieved rows.
            }
        });
    });
};

// Application
app.get('/', (request, response) => {
    function getData() {
        var res = [];
        return db.eachAsync('SELECT column_a, column_b FROM trips group by column_a', (row) => {
            res.push({
                a: row['column_a'],
                b: row['column_b']
            });
        }).then(n => res);
    }
    getData().then(results => {
        console.log(results);
    }).catch(error => {
        console.log(error);
    });
});

2. Only a completion error causes promise rejection - iteration errors are exposed to your application code 2.只有完成错误会导致承诺被拒绝-迭代错误会暴露给您的应用程序代码

// Promisification
db.eachAsync = function(sql, iterationCallback) {
    return new Promise(function(resolve, reject) {
        db.each(sql, iterationCallback, (completionError, n) => {
            if(completionError) {
                reject(completionError);
            } else {
                resolve(n); // the number of retrieved rows.
            }
        });
    });
};

// Application
app.get('/', (request, response) => {
    function getData() {
        var res = [];
        return db.eachAsync('SELECT column_a, column_b FROM trips group by column_a', (iterationError, row) => {
            // You can choose what to do on iterationError.
            // Here, nulls are injected in place of values from the db,
            // but you might choose not to push anything.
            res.push({
                a: iterationError ? null : row['column_a'],
                b: iterationError ? null : row['column_b']
            });
        }).then(n => res);
    }
    getData().then(results => {
        console.log(results);
    }).catch(error => {
        console.log(error);
    });
});

(2) is the better approach because exposing iteration errors affords you more flexibility. (2)是更好的方法,因为暴露迭代错误可以为您提供更大的灵活性。 For example, you could choose to promisify with (2), and emulate (1) in your application : 例如,您可以选择以(2)表示形式,并在应用程序中模拟(1):

// Application
app.get('/', (request, response) => {
    function getData() {
        var res = [];
        var e = null;
        return db.eachAsync('SELECT column_a, column_b FROM trips group by column_a', (iterationError, row) => {
            if(iterationError && !e) {
                // remember the first iteration error
                e = iterationError; 
            } else {
                // push only on success
                res.push({
                    a: row['column_a'],
                    b: row['column_b']
                });
            }
        }).then(n => {
            if(e) {
                throw e;
            } else {
                return res;
            }
        });
    }
    getData().then(results => {
        console.log(results);
    }).catch(error => {
        console.log(error);
    });
});

With (1), by rejecting on first iteration error rather than exposing iteration errors, the same flexibility is not available. 对于(1),通过拒绝第一次迭代错误而不是暴露迭代错误,就没有相同的灵活性。 (1) could not fully emulate (2). (1)无法完全模仿(2)。

Fortunately, the preferred approach (2) is the same as would be obtained with Bluebird's .promisify() method : 幸运的是,首选方法(2)与Bluebird的.promisify()方法相同:

Promise.promisify(db.each); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM