Async / await does not properly "waiting"

I’m trying to resize + upload a user selected picture, get the uploaded URL, and then save the URL to MongoDB.

Turns out the URL submitted to database is always null because it takes time for upload to callback and return the URL, so I decided to use async/await to “wait” for the URL and then submit to DB.

However I can’t get it to “wait” for the return and I can’t find out why. Below is my code:

@observer export default class savePic extends Component {

	async uploadPic(url) {
		const uploader = new Slingshot.Upload("picUpload");

		uploader.send(url, function (error, downloadURL) {
			if (error) {
				throw uploader.xhr.response;
			}
			else {
				console.log('2');
				return Promise.resolve(downloadURL);
			}
		});
	}

	async handleSubmit(e){
		e.preventDefault();
		console.log('1');
		const picURL = await this.uploadPic(store.header.filepath);
		console.log('3');

		const newDoc = await JSON.parse(JSON.stringify(Object.assign({}, {
			'doc': picURL,
            //some other fields
			},
            { /* some other objects */ }
		)));
		console.log('4');

		//call server method to save URL in DB
		newClaim.call(newDoc);

Turns out on console it always comes out 1, 3, 4, 2, and then on server side error because URL is null
May I know why it is not working? Any help is appreciated, thanks!

Promise is a constructor, an async method returns a new instance of a Promise. So you would want to do

function uploadPic(url) {
   return new Promise((resolve, reject) => {
    const uploader = new Slingshot.Upload("picUpload");

		uploader.send(url, function (error, downloadURL) {
			if (error) {
				return reject(uploader.xhr.response);
			}
			else {
				return resolve(downloadURL);
			}
		});
  });
}

Note that if the uploader method didn’t take a callback and instead returned a promise, you wouldn’t have to return a Promise yourself. By default an async function already returns a Promise automatically for you, BUT if your method uses a callback somewhere inside of it as well then that automatic return of a Promise doesn’t work and you need to manually do it. So if uploader.send used a Promise instead, (you could achieve this using a “promisify” package) you’d be able to do this

async uploadPic(url){ 
    const uploader = new Slingshot.Upload("picUpload");

	const downloadUrl = await promisify(uploader.send(url);
    return downloadUrl;			
}
2 Likes

Works like a charm! Thanks!