使用 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);
});
});
};
上面,我有两个异步函数 generateMovieMetaData
和 generateMusicMetaData
它们每个都有 Promise.mapSeries
逻辑
当我自己调用它们时,它们可以正常工作而不会抛出错误。
我想像这样在复合函数中链接这两个函数
var generateMetaData = function(){
generateMusicMetaData()
.then(() => generateMovieMetaData());
}
generateMetaData();
从第一个函数调用 generateMetaData
returns TypeError: Cannot read property 'push' of undefined
错误:
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。我将 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();
});
}
原始实现存在许多问题,因此它们不会 return 当每个函数中的所有异步操作完全完成时,承诺就会得到解决。因此,您无法将它们与其他异步操作协调。
所以首先让我们修复你的两个函数的实现。只使用 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;
}
请注意,此实现中根本没有简单的回调。一切都是使用承诺和异步操作完成的,这些承诺和异步操作自然不会 return 承诺是 "promisified"。我在这里使用了 Bluebird,因为你已经展示了你正在使用它,但你也可以使用现在内置于 node.js 中的 util.promisify()
。
现在,我们有了它,所以这些函数中的每一个 return 都是一个用 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);
});
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);
});
});
};
上面,我有两个异步函数 generateMovieMetaData
和 generateMusicMetaData
它们每个都有 Promise.mapSeries
逻辑
当我自己调用它们时,它们可以正常工作而不会抛出错误。
我想像这样在复合函数中链接这两个函数
var generateMetaData = function(){
generateMusicMetaData()
.then(() => generateMovieMetaData());
}
generateMetaData();
从第一个函数调用 generateMetaData
returns TypeError: Cannot read property 'push' of undefined
错误:
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。我将 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();
});
}
原始实现存在许多问题,因此它们不会 return 当每个函数中的所有异步操作完全完成时,承诺就会得到解决。因此,您无法将它们与其他异步操作协调。
所以首先让我们修复你的两个函数的实现。只使用 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;
}
请注意,此实现中根本没有简单的回调。一切都是使用承诺和异步操作完成的,这些承诺和异步操作自然不会 return 承诺是 "promisified"。我在这里使用了 Bluebird,因为你已经展示了你正在使用它,但你也可以使用现在内置于 node.js 中的 util.promisify()
。
现在,我们有了它,所以这些函数中的每一个 return 都是一个用 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);
});