异步等待 Promise 的使用在等待时会阻塞程序。

3

WebSocket连接的事件处理程序(wss.on)被调用,将调用一个异步函数sendInitialState()。在这里我调用了一个异步函数(getMetadata()),它返回一个Promise,在其中我做了一些异步操作。我解决了这个承诺,调用了resolve(res),但是程序仍然在等待await的回应,也就是说,console.log(metadata)从未被调用。

wss.on('connection', (ws) => {

    sendInitialState();
});

const sendInitialState = async (ws) => {

    console.log('Sending initial state!');
    const metadata = await getMetadata();
    console.log('metadata:', metadata);
};

const getMetadata = async () => {

    return new Promise((resolve, reject) => {
        MongoClient.connect(url, (err, db) => {
            if (err) {reject(err);}
            else {
              db.collection("meta").findOne({type: 'a'}, (err, res) => {
                if (err) reject(err);
                else {
                    db.close();
                    console.log('Metadata found: ', res);
                    resolve(res);
                }

              });
            }
        }); 
    });

}

我的程序为什么会阻塞?

(我尝试在外部的(ws) =>之前添加async,但结果是相同的)


3
首先,if (err) throw err 是错误的,应该改为 if (err) return reject(err),在你使用它的两个地方都要修改。以后请注意,if (err) throw err 几乎永远不应该出现在你的代码中,我从未遇到过这是正确的错误处理情况。然后,你需要在 await 周围加上 try/catch,这样可以捕获可能发生的任何错误。在这里不应忽略错误处理,当事情不像预期那样工作时,这可能是你首先应该看的问题。 - jfriend00
2
另外,应该是 console.log('metadata:', blockchainMetadata);。你在日志语句中引用了错误的变量。 - jfriend00
2
@jfriend00 Promise会自动将任何抛出的错误转换为一个被拒绝的Promise,可以在catch()中捕获。为什么在这种情况下使用throw是无效的?难道这只是一个性能问题,因为在throw周围没有优化吗? - peteb
4
那段代码并不在 Promise 回调函数中,而是在数据库异步回调函数中。这里没有自动转换为拒绝状态的过程。 - jfriend00
3
reject()只是普通的函数调用,您的函数会继续执行块中的其余代码。您应该使用常规流程控制来将错误路径中要执行的代码与非错误路径分开。这是常见的JavaScript编程技巧。 - jfriend00
显示剩余10条评论
1个回答

1

尽管在使用reject()而非抛出异常的情况下解决了您的问题,但是您的代码风格可以改进:

  • 将回调转换为Promise时,不需要将函数声明为async:它只会包装成另一个Promise;
  • 最好声明函数而不是将函数表达式赋值给const;
  • 为了减少缩进,模式应该是if (err) { return reject(err); }:这样就不需要else语句;
  • 我们可能想要重用基于Promise的函数,因此提取公共逻辑会导致更易读的代码;

原始问题更多关于Promise而非async/await,因此这是一个重新编写的版本,具有更小的函数、浅缩进以及更多使用async/await。

wss.on('connection', (ws) => {
  sendInitialState();
});

async function sendInitialState(ws) {
  console.log('Sending initial state!');
  const metadata = await getMetadata();
  console.log('metadata:', metadata);
}

function connect(url) {
  return new Promise((resolve, reject) => {
    MongoClient.connect(url, (err, db) => {
      if (err) {
        return reject(err);
      }
      resolve(db);
    });
  });
}

function findOne(db, collectionName, object) {
  return new Promise((resolve, reject) => {
    db.collection(collectionName).findOne(object, (err, res) => {
      if (err) {
        return reject(err);
      }
      console.log('Metadata found: ', res);
      resolve(res);
    });
  });
}

async function getMetadata() {
  const db = await connect(url);
  const res = await findOne(db, 'meta', {
    type: 'a'
  });
  db.close();
  console.log('Metadata found: ', res);
  return res;
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接