0

Ich versuche, Tracks über die Soundcloud-API herunterzuladen und starte dann einen Callback, sobald eine unbestimmte Anzahl von Tracks heruntergeladen wurde. Wenn ich den unten stehenden Code ausführe, sehe ich "All done" vor allem anderen als Konsole protokolliert, obwohl ich vorhabe, dass es das letzte ist ... Was mache ich falsch?Was ist los mit dieser Verwendung von Async erwarten?

// 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 ('/ Tracks', .. . 'ist ein Callback und du wartest nicht darauf, dass es aufgelöst wird, bevor du Promise.all machst. Du musst manuell eine Versprechung machen, die in '(err, tracks) => {...}' verrechnet wird und dann darauf warten Bevor Sie 'Promise.all (trackActions) 'tun –

+0

Ahh, hab es. Danke! Irgendwie ist mir völlig klar, dass natürlich eine API-Aktion async ist ... –

+0

@MeirionHughes Irgendeine Idee, wie man das schreibt, gegeben dass das Schreiben der Datei über eine Stream-Pipe erfolgt? Daran kann ich meinen Kopf nicht halten. –

Antwort

0

Ich denke, der einfachste Weg, Promise.all nach tracks.map Schleife beendet zu bewegen wäre.

Eine elegantere Lösung wäre, auch SC.get Promis zu machen und async await entlang Ihres gesamten Codes zu verwenden.

UPDATE:

Könnte es nicht testen, so dass nicht sicher, ob es funktioniert, aber es wäre so etwas wie dieses:

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' 
}) 

Mit async await:

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 
}) 

Wenn Sie nur möchte Versprechen verwenden:

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) wartet auf was auch immer Versprechen sind in trackActions, aber trackActions ist leer zu der Zeit, die Sie den Anruf tätigen. Sie fügen dem Array nur Versprechungen hinzu, nachdem Ihr Rückruf SC.get aufgerufen wird.

Versuchen Sie, Ihre Promise.all... Block innerhalb des SC.get Rückruf wie folgt setzen:

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')) 
    }) 
    } 
}) 

Es ist erwähnenswert, wie gut, dass Ihre Linie throw new Error(err) das Programm stürzt ab, da nirgendwo gibt es für die Fehler abgefangen werden.

Wie Antonio Val erwähnt, gibt es bessere Möglichkeiten, dies zu tun. Wenn Sie den Knoten-Bibliothek dann soundcloud der letzte Teil des Codes promisify könnte wie folgt aussehen:

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. 
}) 

oder innerhalb eines 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 
}