使用异步等待有什么问题?

时间:2017-03-12 16:06:28

标签: node.js asynchronous async-await es6-promise ecmascript-2017

我正在尝试通过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'))
})

2 个答案:

答案 0 :(得分:1)

Promise.all(trackActions)等待trackActions中的任何承诺,但trackActions在您拨打电话时为空。您只需在调用SC.get回调后向阵列添加承诺。

尝试将Promise.all...块放在SC.get回调中,如下所示:

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所说,有更好的方法可以做到这一点。如果您宣传node-soundcloud库,那么代码的最后部分可能如下所示:

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
}

答案 1 :(得分:0)

我认为最简单的方法是在Promise.all循环结束后移动tracks.map

更优雅的解决方案是宣传SC.get,并在所有代码中使用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'
})

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

如果你只想使用Promises:

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