Проблема с Node JS и обещания возвращаются до финиша - PullRequest
1 голос
/ 31 января 2020

Я пытаюсь получить последний загруженный список видео с указанным c идентификатором канала Youtube со статистикой. Я получаю список, но не статистику.

Я думаю, что результат возвращается до назначения объекта. Как я могу вернуть результат после назначения объекта?

Это мой код:

async function (inputs, exits) {

    var { google } = require('googleapis');
    var youtubeApiKey = 'API_KEY';
    var service = google.youtube('v3');

    // Get upload ID to list uploaded videos
    service.channels.list({
        part: 'contentDetails',
        id: inputs.ytbIdChannel,
        key: youtubeApiKey,
    })
        .then(res => {
            if (res.data.items[0].contentDetails.relatedPlaylists.uploads) {
                let uploadsId = res.data.items[0].contentDetails.relatedPlaylists.uploads;

                // With the uploadID, get all uploaded videos
                service.playlistItems.list({
                    part: 'snippet',
                    playlistId: uploadsId,
                    maxResults: '12',
                    key: youtubeApiKey,
                })
                    .then(res => {
                        this.youtbeVideos = res.data.items,

                        // For each videos in list, inject the statistics object and return all videos
                        this.youtbeVideos.forEach(video => {
                            let videoId = video.snippet.resourceId.videoId

                            service.videos.list({
                                part: 'statistics',
                                id: videoId,
                                key: youtubeApiKey,
                            })
                                .then(res => {
                                    Object.assign(video, res.data.items[0].statistics)
                                    return exits.success(
                                        this.youtbeVideos
                                    );
                                })
                                .catch(error => {
                                    console.error(error);
                                });
                        });



                    })
                    .catch(error => {
                        console.error(error);
                    });
            }
        })
        .catch(error => {
            console.error(error);
        });

}

Спасибо за помощь

Ответы [ 3 ]

1 голос
/ 31 января 2020

Я исправил ваш код и убедился, что он работает с API YouTube.

Сначала создайте асинхронную функцию, заменив:

service.channels.list({
    part: 'contentDetails',
    id: inputs.ytbIdChannel,
    key: youtubeApiKey,
})
    .then(res => {

на:

service.channels.list({
    part: 'contentDetails',
    id: inputs.ytbIdChannel,
    key: youtubeApiKey,
})
    .then(async res => {

Затем для исправления необходимо заменить содержимое блока if (res.data.items[0].contentDetails.relatedPlaylists.uploads) следующим кодом:

let uploadsId = channels.data.items[0].contentDetails.relatedPlaylists.uploads;

// With the uploadID, get all uploaded videos
let videos = await service.playlistItems.list({
    part: 'snippet',
    playlistId: uploadsId,
    maxResults: '12',
    key: youtubeApiKey,
})
this.youtbeVideos = videos.data.items;

// For each videos in list, inject the statistics object and return all videos
for (let video of this.youtbeVideos) {
    let videoId = video.snippet.resourceId.videoId

    let stats = await service.videos.list({
        part: 'statistics',
        id: videoId,
        key: youtubeApiKey,
    })
    Object.assign(video, stats.data.items[0].statistics)
}

return exits.success(
    this.youtbeVideos
);

Два ключевых изменения, которые я сделал:

  1. Использование ожидает , так что итерация по this.youtbeVideos приостанавливает получение статистики, которую нужно извлечь, прежде чем перейти на
  2. Перемещение возврата и вызов exits.success вне l oop чтобы он возвращал данные один раз после добавления всей статистики

Вы можете запустить мою рабочую копию здесь - просто добавьте свой ключ API для YouTube.

Когда список вернется, он будет содержать всю статистику, как и ожидалось.

0 голосов
/ 31 января 2020

вероятно, вы должны вернуть или «дождаться» обещаний. Не просто позвонить им. В противном случае никто не знает, когда функция готова.

пишите await перед каждым service.XXX.list, сделайте ваши функции обратного вызова async и используйте for loop или promise.all вместо forEach для this.videos потому что в противном случае вы не можете ждать каждое обещание ( ожидайте в течение forEach )

И, возможно, это исправить:

  • вы звоните return exits.success(this.youtbeVideos); для каждое видео, не должно ли быть для всех видео сделано?
  • Object.assign(video, res.data.items[0].statistics), вероятно, назначает каждый раз одни и те же клавиши (перезаписывает)

заявление об отказе: я не знаю API / вашу среду вызовов, так что это только предложения, которые, возможно, будут полезны.

0 голосов
/ 31 января 2020

Может быть, вы можете изменить .then для async / await.

Например:

service.getAll()
  .then(res => {
      service.getById(res.id)
        .then(res =>{
           .....
       })
    })

Вы можете использовать await:

const res1 = await service.getAll();
const res2 = await service.getById(res1.id);

Это более чистый код и с помощью await узел останавливает все шаги и ожидает его. Когда fini sh, go перейдет к следующему шагу ... и затем снова .. ждать, выполнить, ждать, выполнить.

...