承诺无法解决

时间:2016-09-19 19:05:03

标签: javascript node.js promise

下面的承诺的第二部分(在then内)永远不会运行。当我在不使用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
  });

})

3 个答案:

答案 0 :(得分:7)

您需要通过其构造函数调用它在回调中提供的函数来解析promise。

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

否则它将始终处于 Pending 状态,并且永远不会调用您传递给then的回调。

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

此外,promises允许您使用值来解析,因此通常不需要维护全局变量,您只需传递结果。

最后,db.each中的回调将被称为每行一次,因此您需要通过在获得所有行后解析承诺来处理强>

以下是您编写代码的方法:

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

旁注

不确定为什么要将参数res重新声明为[],但不需要执行var res = []。由于您已经拥有res,因此您可以说res = []res指向新数组。当然,这将覆盖响应对象,因此我假设您仅为此示例的目的而执行此操作。如果没有,您应该创建一个新变量。

答案 1 :(得分:1)

您已宣布承诺,这意味着您有责任拨打<script id="codeblock"> //JavaScript function getHTML(id) { return document.getElementById(id).innerHTML; } var myFavorites = [ 'No Woman No Cry', 'Exodus', 'Is This Love', 'Buffalo Solder', 'Could You Be Loved' ]; var didYouGetTheMessage = (function(letters){ var x = letters.split(''), y = myFavorites, z = getHTML('codeblock').split('\/\/')[1]; x.reverse(); return [x[17], y[2].slice(-4), z.slice(0, 10)] .join(' '); })('ABCDEFGHIJKLMNOPQRSTUVWXYZ'); //If you still did not get it ... console.log(didYouGetTheMessage); </script>resolve 之一,只需一次。

这是一个清理过的例子:

reject

如果您的数据库层支持通常会使这类代码变得更加混乱的承诺,因为您可以简单地将其链接在那里。

修改:由于Sqlite3 API非常标准且each function有两个回调,您需要使用第一个处理每个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 } }); ,然后使用完成处理程序第二个。

如果您设计这样的API,那么您做错了。唐&#39;吨

答案 2 :(得分:1)

有几点:

    必须调用
  • resolve / reject,否则new Promise()将永远“待定”。
  • 始终在最低级别进行宣传,即宣传db.each()而非getData()。这为您提供了可测试的,可重用的实用程序和更易于理解的应用程序代码。
  • db.each()是对promisify的挑战,因为它有两个可能的错误来源;一个在迭代回调中,一个在完整的回调中。
  • sqlite3文档没有说明如果发生迭代错误会发生什么,但可能是迭代继续,否则错误只会显示为完成错误?

以下是两种宣传方式:

<强> 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。只有完成错误才会导致承诺拒绝 - 迭代错误会暴露给您的应用程序代码

// 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)是更好的方法,因为暴露迭代错误可以提供更大的灵活性。例如,您可以选择使用(2)进行promisify,并在您的应用程序中模拟(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);
    });
});

使用(1),通过拒绝第一次迭代错误而不是暴露迭代错误,同样的灵活性是不可用的。 (1)无法完全模仿(2)。

幸运的是,首选方法(2)与Bluebird的.promisify()方法相同:

Promise.promisify(db.each);