簡體   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);
    });
  });
};

上面,我有兩個異步函數generateMovieMetaDatagenerateMusicMetaData它們每個都有Promise.mapSeries邏輯

當我自己調用它們時,它們可以正常工作而不會拋出錯誤。

我想像這樣將這兩個函數鏈接在一個復合函數中

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

調用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)


編輯 1:好的,我更改了代碼以使其看起來更容易。 我得到同樣的錯誤不知道為什么
日志顯示兩個函數中的bluebird.mapSeries同時發生,然后拋出錯誤

我沒有解決每個功能中的承諾,我只有回報。 我更改了return 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);
  ...
  });
};

然后我有

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

原始實現存在許多問題,因此當每個函數中的所有異步操作完全完成時,它們不會返回已解決的承諾。 因此,您無法將它們與其他異步操作協調起來。

所以首先讓我們修復你的兩個函數的實現。 只使用 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;
}

請注意,此實現中根本沒有簡單的回調。 一切都是使用承諾和異步操作完成的,不會自然返回承諾的是“承諾的”。 我在這里使用了 Bluebird,因為你已經展示了你正在使用它,但你也可以使用現在內置到 node.js 中的util.promisify()

現在,我們有了它,所以這些函數中的每一個都返回一個用 json 解析的承諾,並且只有在內部的所有異步操作完成時才解析。 這為使用 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);
}); 

但是,由於這些似乎是兩個完全不相關的操作,您可以並行執行它們:

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