0

soundcloud API経由でトラックをダウンロードしようとしていて、不確定なトラックがダウンロードされた後にコールバックを開始しようとしています。私が下のコードを実行すると、最後のものであることを意図しているにもかかわらず、 "すべて完了"が何かの前にコンソールに記録されているのが見えます...何が間違っていますか?この非同期待ち時間の問題は何ですか?

// Deps 
import fs from 'fs' 
import SC from 'node-soundcloud' 
import request from 'request' 

// Write mp3 function 
function writeMP3(track) { 
    return new Promise((resolve, reject) => { 

    console.log('Starting download: ', track.title) 

    request.get(track.download_url) 
    .on('error', err => { 
     // reject('Download error: ', err) 
    }) 
    .on('finish',() => { 
    () => resolve('Download complete') 
    }) 
    .pipe(fs.createWriteStream(`./data/temp/${track.title}_${track.user.username}.mp3`)) 

    }) 
} 

async function asyncTrackFetch(track) { 
    return await writeMP3(track) 
} 

// Array of promises to callback upon 
const trackActions = [] 

SC.init({ 
    id: 'MY_ID', 
    secret: 'MY_SECRET' 
}) 

SC.get('/tracks', (err, tracks) => { 

    if (err) { 
    throw new Error(err) 
    } else { 

    console.log('Tracks fetched: ', tracks.length) 

    tracks.map(track => { 

     if (track.downloadable) { 
     console.log('downloadable') 

     trackActions.push(asyncTrackFetch(track)) 

     } 

    }) 
    } 
}) 

// Perform requests async 
Promise.all(trackActions).then(() => { 
    console.log('All done') 
    console.log(fs.readdirSync('./data/temp')) 
}) 
+0

'SC.get( '/トラックの内側に..あなたがPromise.allを実行する前に解決するのを待っているわけではありません。 '(err、tracks)=> {...}'で解決する約束を手動で行う必要があります。あなたが 'Promise.all(trackActions)'を実行する前に –

+0

ああ、ありがとう!どういうわけか、私の心は、APIアクションが非同期であるということを完全に滑ってしまった... –

+0

@MeirionHughesファイルの書き込みがストリーム 'pipe'を介して行われていることを確認しますか?その周りに私の頭を包むことはできません。 –

答えて

0

私はtracks.mapループが終了した後、最も簡単な方法は、Promise.allを移動することだと思います。

より洗練された解決策は、SC.getをお約束し、すべてのコードに沿ってasync awaitを使用することです。

UPDATE:

はそれが動作するかどうかわからないので、それをテストすることができませんでしたが、それはこのようなものになるだろう:ちょうどあなたの場合は

async function start() { 
    const tracks = await getTracks(); 

    for (let track of tracks) { 
    await writeMP3(track) 
    } 
} 

start() 
.then(() => { 
    console.log('All done') 
    console.log(fs.readdirSync('./data/temp')) 
}) 
.catch((err) => { 
    // insert error handler here 
}) 

async await

import fs from 'fs' 
import SC from 'node-soundcloud' 
import request from 'request' 

function writeMP3(track) { 
    return new Promise((resolve, reject) => { 

    console.log('Starting download: ', track.title) 

    request.get(track.download_url) 
    .on('error', err => { 
     // reject('Download error: ', err) 
    }) 
    .on('finish',() => { 
    () => resolve('Download complete') 
    }) 
    .pipe(fs.createWriteStream(`./data/temp/${track.title}_${track.user.username}.mp3`)) 

    }) 
} 

function getTracks() { 
    return new Promise((resolve, reject) => { 

    SC.get('/tracks', (err, tracks) => { 
     if (err) { 
     return reject(err) 
     } 

     console.log('Tracks fetched: ', tracks.length) 
     resolve(tracks) 
    }) 
    }) 
} 

SC.init({ 
    id: 'MY_ID', 
    secret: 'MY_SECRET' 
}) 

約束を使用したい:

getTracks 
.then((tracks) => { 
    const promiseArray = tracks.map((track) => { 
    return writeMP3(track) 
    }) 
    return Promise.all(promiseArray) 
}) 
.then(() => { 
    console.log('All done') 
    console.log(fs.readdirSync('./data/temp')) 
}) 
.catch((err) => { 
    // insert error handler here 
}) 
1

Promise.all(trackActions)trackActionsにあるすべての約束を待ちますが、電話をかけたときにtrackActionsは空です。 SC.getコールバックが呼び出された後で、配列に約束を追加するだけです。

は、このようなSC.getコールバックの内側にあなたのPromise.all...ブロックを入れてみてください:

SC.get('/tracks', (err, tracks) => { 

    if (err) { 
    throw new Error(err) 
    } else { 

    console.log('Tracks fetched: ', tracks.length) 

    tracks.map(track => { 

     if (track.downloadable) { 
     console.log('downloadable') 

     trackActions.push(asyncTrackFetch(track)) 

     } 

    }) 

    Promise.all(trackActions).then(() => { 
     console.log('All done') 
     console.log(fs.readdirSync('./data/temp')) 
    }) 
    } 
}) 

それだけでなく、キャッチする、そのエラーのためにどこにもありませんので、あなたのラインthrow new Error(err)がプログラムをクラッシュすることを言及する価値があります。

アントニオ・ヴァル(Antonio Val)が述べたように、これを行うより良い方法があります。あなたはノードてSoundcloudライブラリをpromisify場合は、あなたのコードの最後の部分は、次のようになります。

SC.get('/tracks').then(tracks => { 
    // No need for trackedActions array. 
    return Promise.all(tracks.filter(track => track.downloadable) 
    .map(track => asyncTrackFetch(track))) 
}).then(fetchedTracks => { 
    console.log('All done fetching tracks', fetchedTracks) 
}).catch(err => { 
    // Handle error. 
}) 

またはasync function

try { 
    const tracks = await SC.get('/tracks') 
    const fetchPromises = tracks 
    .filter(track => track.downloadable) 
    .map(track => asyncTrackFetch(track)) 
    const fetchedTracks = await Promise.all(fetchPromises) 

    console('All done fetching tracks.', fetchedTracks) 
} catch (err) { 
    // Handle error 
} 
関連する問題