简体   繁体   English

使用 promises Nodejs 链接异步函数

[英]Chaining async functions with promises Nodejs

var fs = require('fs');
var node_dir = require('node-dir');
var bluebird = require('bluebird');
var moviesClient = new ApiClient(...)
var musicClient = new ApiClient(...)
var lib = require('./index.js');

var generateMovieMetaData = async function(){
  var json = { movies: [] };

  node_dir.files(path, function(err, files) {

    bluebird.mapSeries(files, function(file){

    return moviesClient.send(new lib.requests.Movie(file))
       .then((movie) => {
      // movie is json
      /* do some loops and work with the movie json*/
      json.movies.push(movie);
       });
    })
    .then(function(movies){
      fs.writeFile('./movies.json', JSON.stringify(json), 'utf8', (err)=>{
         if(err) console.log(err)
         else { 
          console.log('File saved');
          }
      })
      return json; // go to the next function if any
    })
    .catch(function(err){
      console.log("Movie metadata could not be generated due to some error", err);
    });
  });
};

var generateMusicMetaData = async function(){
  var json = { music: [] };

  node_dir.subdirs(config.music.path, function(err, subdirs) {
    if (err) throw err;

    bluebird.mapSeries(subdirs, function(dir){

    return musicClient.send(new lib.requests.Album(dir))
      .then((album) => {
      // album is json
      /* do some loops and work with the album json*/
      json.music.push(album);
      });
    })
    .then(function(music){
      fs.writeFile('./music.json', JSON.stringify(json), 'utf8', (err)=>{
         if(err) console.log(err)
         else { 
          console.log('File saved');
          }
      })
      return json; // go to the next function if any
    })
    .catch(function(err){
      console.log("Album metadata could not be generated due to some error", err);
    });
  });
};

Above, I have two async functions generateMovieMetaData and generateMusicMetaData each of them has Promise.mapSeries logic上面,我有两个异步函数generateMovieMetaDatagenerateMusicMetaData它们每个都有Promise.mapSeries逻辑

When I call them on their own, they work correctly without throwing errors.当我自己调用它们时,它们可以正常工作而不会抛出错误。

I want to chain the two functions in a composite function like this我想像这样将这两个函数链接在一个复合函数中

var generateMetaData = function(){
  generateMusicMetaData()
   .then(() => generateMovieMetaData());
}
generateMetaData(); 

Calling generateMetaData returns TypeError: Cannot read property 'push' of undefined error from the first function:调用generateMetaData返回TypeError: Cannot read property 'push' of undefined error from the first function:

Album metadata could not be generated due to some error TypeError: Cannot read property 'push' of undefined
[0]     at musicClient.send.then (/mnt/c/Users/ridhwaan/Source/homehost/server.js:179:23)
[0]     at tryCatcher (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/util.js:16:23)
[0]     at Promise._settlePromiseFromHandler (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:51
2:31)
[0]     at Promise._settlePromise (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:569:18)
[0]     at Promise._settlePromise0 (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:614:10)
[0]     at Promise._settlePromises (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:693:18)
[0]     at Async._drainQueue (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/async.js:133:16)
[0]     at Async._drainQueues (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/async.js:143:10)
[0]     at Immediate.Async.drainQueues [as _onImmediate] (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/a
sync.js:17:14)
[0]     at runCallback (timers.js:756:18)
[0]     at tryOnImmediate (timers.js:717:5)
[0]     at processImmediate [as _immediateCallback] (timers.js:697:5)


EDIT 1: Ok I changed the code to look easier. 编辑 1:好的,我更改了代码以使其看起来更容易。 Am getting the same error dunno why 我得到同样的错误不知道为什么
Logging shows that bluebird.mapSeries in both functions is happening at the same time, then throwing the error 日志显示两个函数中的bluebird.mapSeries同时发生,然后抛出错误

I wasnt resolving a promise in each function, I only had a return.我没有解决每个功能中的承诺,我只有回报。 I changed the line where I have return json;我更改了return json; to resolve(json); resolve(json);

var generateMusicMetaData = function() {
  return new Promise(function(resolve, reject) {
  ...
  r̶e̶t̶u̶r̶n̶ ̶j̶s̶o̶n̶;̶
  resolve(json);
  ...
  });
};

var generateMovieMetaData = function() {
  return new Promise(function(resolve, reject) {
  ...
  r̶e̶t̶u̶r̶n̶ ̶j̶s̶o̶n̶;̶
  resolve(json);
  ...
  });
};

then i have然后我有

var generateMetaData = function(){
  generateMusicMetaData()
    .then(function(result) { 
      return generateMovieMetaData();
    });
}

There are a number of problems with the original implementations that make it so they don't return a promise that is resolved when all the async operations in each function are completely done.原始实现存在许多问题,因此当每个函数中的所有异步操作完全完成时,它们不会返回已解决的承诺。 Because of that, you can't coordinate them with other asynchronous operations.因此,您无法将它们与其他异步操作协调起来。

So first let's fix the implementation of your two functions.所以首先让我们修复你的两个函数的实现。 It's a lot simpler to just use ES6 async/await for that implementation:只使用 ES6 async/await 来实现要简单得多:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const node_dir = Promise.promisifyAll(require('node-dir'));
let moviesClient = new ApiClient(...)
let musicClient = new ApiClient(...)
let lib = require('./index.js');


async function generateMovieMetaData(path) {
    var json = { movies: [] };

    let files = await node_dir.filesAsync(path);

    for (let f of files) {
        let movie = await moviesClient.send(new lib.requests.Movie(f));
        json.movies.push(movie);
    }

    await fs.writeFileAsync('./movies.json', JSON.stringify(json), 'utf8').catch(err => {
        console.log("Movie metadata could not be generated due to some error", err);
        throw err;
    });
    return json;
}

async generateMusicMetaData function(path) {
    var json = { music: [] };

    let files = await node_dir.subdirsAsync(path);

    for (let d of dirs) {
        let album = await musicClient.send(new lib.requests.Album(d));
        json.music.push(album);
    }

    await fs.writeFileAsync('./music.json', JSON.stringify(json), 'utf8').catch(err => {
        console.log("Album metadata could not be generated due to some error", err);
        throw err;
    });
    return json;
}

Note that there are no plain callbacks at all in this implementation.请注意,此实现中根本没有简单的回调。 Everything is done using promises and asynchronous operations that don't naturally return promises were "promisified".一切都是使用承诺和异步操作完成的,不会自然返回承诺的是“承诺的”。 I used Bluebird here for that since you already showed you were using it, but you could have also used util.promisify() which is built into node.js now.我在这里使用了 Bluebird,因为你已经展示了你正在使用它,但你也可以使用现在内置到 node.js 中的util.promisify()

Now, we have it so each of these functions returns a promise that resolves with the json and onlyh resolves when all the asynchronous operations inside are done.现在,我们有了它,所以这些函数中的每一个都返回一个用 json 解析的承诺,并且只有在内部的所有异步操作完成时才解析。 That sets it up well for sequencing using async/await:这为使用 async/await 进行排序做好了准备:

function async generateMetaData(path){
    let music = await generateMusicMetaData(path);
    let movie = await generateMovieMetaData(path);
    return {music, movie};
}

generateMetaData(path).then(results => {
    console.log(results);
}).catch(err => {
    console.log(err);
}); 

But, since these appear to be two completely unrelated operations, you could do them in parallel:但是,由于这些似乎是两个完全不相关的操作,您可以并行执行它们:

function generateMetaData(path){
    return Promise.all([generateMusicMetaData(path), generateMovieMetaData(path)]).then(([music, movie]) => {
        return {music, movie};
    });
}

generateMetaData(path).then(results => {
    console.log(results);
}).catch(err => {
    console.log(err);
}); 

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

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