承诺在测试开始前得到解决

Promise being resolved before the beginning of test

解决方案

Converting the promises into the promise factory 修复了日志出现在测试 运行 之前的问题。我的 promise.all 没有解决,因为 I just assumed NeDB 数据库初始化函数遵循回调模式而没有 properly going through the documentation。再次查看文档后,我了解到它没有。从 promise 工厂中删除回调解决了这个问题。

问题

我有一个承诺链,其中 Promise,如果解决,returns 和 Promise.allPromise.all 接受了 3 个承诺。这是我的代码:

lib.init = (dir) => {
  // check if the .data directory exists
  return new Promise((resolve, reject) => {
    if(dir) lib.baseDir = path.join(__dirname, '../', dir)
    console.log('DATA LIBRARY INITIALIZATION: working directory - ', lib.baseDir)
    fs.stat(lib.baseDir, (error, stats) => {
      if(!error){
        console.log('DATA LIBRARY INITIALIZATION: successfully retrieved file stats.')
        if(!stats.isDirectory()) {
          console.log('DATA LIBRARY INITIALIZATION: the base directory does not exist. Creating base directory now.')
          fs.mkdir(lib.baseDir, (err) => {
            if(!err) {
              console.log('DATA LIBRARY INITIALIZATION: base directory created successfully.')
              resolve()
            } else reject('Could not create the data directory.')
          })
        } else resolve()
      } else {
        console.log('DATA LIBRARY INITIALIZATION: could not retrieve file stats. Creating base directory now.')
        fs.mkdir(lib.baseDir, (err) => {
          if(!err) {
            console.log('DATA LIBRARY INITIALIZATION: base directory created successfully.')
            resolve()
          } else reject('Could not create the data directory.')
        })
      }
    })
  }).then(() => {
    console.log('DATA LIBRARY INITIALIZATION: initializing databases.')
    return Promise.all([loadComponents, loadShows, loadScreens])
  })
}

以下是作为参数传递的 3 个承诺:

const loadShows = new Promise((resolve, reject) => {
  // for saving profiles (set of screens designed/configured for different shows)
  console.log('DATA LIBRARY INITIALIZATION: initializing SHOWS collection.')
  lib.shows = new Datastore({ filename: path.join(lib.baseDir, 'shows.db'), autoload: true }, error => {
    if(!error) {
      console.log('DATA LIBRARY INITIALIZATION: successfully initialized SHOWS collection.')
      resolve()
    } else reject(`Could not load shows. Error: ${error.message}`)
  })
})

const loadScreens = new Promise((resolve, reject) => {
  // for saving screen settings (list of component settings)
  console.log('DATA LIBRARY INITIALIZATION: initializing SCREENS collection.')
  lib.screens = new Datastore({ filename: path.join(lib.baseDir, 'screens.db'), autoload: true }, error => {
    if(!error) {
      console.log('DATA LIBRARY INITIALIZATION: successfully initialized SCREENS collection.')
      resolve()
    } else reject(`Could not load screens. Error: ${error.message}`)
  })
})

const loadComponents = new Promise((resolve, reject) => {
  // for saving components (default settings for each component)
  console.log('DATA LIBRARY INITIALIZATION: initializing COMPONENTS collection.')
  lib.components = new Datastore({ filename: path.join(lib.baseDir, 'components.db'), autoload: true }, error => {
    if(!error) {
      console.log('DATA LIBRARY INITIALIZATION: successfully initialized COMPONENTS collection.')
      resolve()
    } else reject(`Could not load components. Error: ${error.message}`)
  })
})

这是我的测试文件:

let chai = require('chai')
let chaiAsPromised = require('chai-as-promised')
chai.use(chaiAsPromised).should()

let _data = require('../lib/data')

describe('data library', () => {
  describe('#init', () => {
    it('should be able to initialize the collections without error', () => {
      return _data.init('testData').should.be.fulfilled
    })
  })

  after(function () {
    // runs once after the last test in this block
    // delete the testData directory and its contents
    return _data.cleanup()
  });
})

这是我在 运行 mocha:

时得到的日志
yarn workspace v1.22.4
yarn run v1.22.4
$ mocha
DATA LIBRARY INITIALIZATION: initializing SHOWS collection.
DATA LIBRARY INITIALIZATION: initializing SCREENS collection.
DATA LIBRARY INITIALIZATION: initializing COMPONENTS collection.


  data library
    #init
DATA LIBRARY INITIALIZATION: working directory -  /home/nm/projects/nightmoves/local-data/testData
DATA LIBRARY INITIALIZATION: could not retrieve file stats. Creating base directory now.
DATA LIBRARY INITIALIZATION: base directory created successfully.
DATA LIBRARY INITIALIZATION: initializing databases.
      1) should be able to initialize the collections without error
DATA LIBRARY CLEANUP: removing everything in -  /home/nm/projects/nightmoves/local-data/testData
DATA LIBRARY INITIALIZATION: cleanup successful.


  0 passing (2s)
  1 failing

  1) data library
       #init
         should be able to initialize the collections without error:
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/home/nm/projects/nightmoves/local-data/test/data.js)
      at listOnTimeout (internal/timers.js:549:17)
      at processTimers (internal/timers.js:492:7)

我不明白的是为什么我看到 promise.all 中通过的 Promises 的日志,甚至在测试 运行 by mocha 之前。

TLDR。

  1. Node.js 执行模块的所有语句 load/require.
  2. 承诺会立即执行,而不仅仅是当有人对他们的结果感兴趣时。

所以你的第二个片段是:

const loadShows = new Promise((resolve, reject) => {

}
const loadXXX = ...

只是创建这些承诺并在程序开始时立即执行(或者至少当模块是 required 或 import-ed 时)。

然后执行 mocha 和其他代码并最终调用等待 Promise.all(...your promises)init,此时它们可能已经完成,因为它们完成得很快。

要解决此问题,请将您的功能更改为“承诺工厂”:

const loadShows = () => new Promise((resolve, reject) => {

}
const loadXXX = () => ...

然后像这样使用它们:

return Promise.all([loadShows(), ...])

或者将它们的声明移动到 init 方法中的 .then 处理程序。只有当你到达这段代码时,它们才会被实例化和执行。