0

I am trying to get a promise function with bluebirdjs. but all attempts falling my way because maybe I don't know what I am doing...?

I want to get files locations, then download the files one after the other then push to an array.

import * as Promise from 'bluebird';
fileFunction(files){
  Promise.map(files, function(file) {
    // Promise.map awaits for returned promises as well.
    //Promise.delay(1000);
     return this.getLocation(file); 
    },
  ).then((file) => {
      console.log('done')
    });

}

getLocation(file){
 if(file){
  return this._storage.ref(file).getDownloadURL().subscribe(url => 
      this.img_array.push(url)
  );
 }

When I call the return this.getLocation(file)... I get the following error bluebird.js:1545 Unhandled rejection TypeError: Cannot read property 'getLocation' of undefined ....bluebird.js

Edit part of the code am using now!

 fileFunction(files){
    return Promise.map(files, file => {
       return this.getDownloadUrl(file); 
    }).then(locations => {
        // add these locations onto this.img_array
        this.img_array.push(...locations);
        console.log('done');
        return locations;
    });
  }


getFiles(e): Promise<any>{
  this.outPutFiles = e;

 this.fileFunction(this.outPutFiles).then(locations => {
  locations.map((arr) => arr.subscribe((files) => this.downloadUrls.push(files)));
  }).catch(err => {
      console.log(err);
  });
}


getDownloadUrl(file){
  if(file){
    return this._storage.ref(file).getDownloadURL();
   } else {
    return Promise.reject(new Error('No file passed to getLocation'));
   }
}
LearnToday
  • 2,762
  • 9
  • 38
  • 67
  • 2
    Try an arrow function for the Promise: `Promise.map(files, (file) => {`…`})`. See [Javascript “this” pointer within nested function](https://stackoverflow.com/a/36526580/4642212). – Sebastian Simon Aug 24 '18 at 04:06
  • Why are you calling the `getLocation` function as member of `this` value? – Ram Aug 24 '18 at 04:08
  • @undefined, my aim is to get the file, where should I call the getLocation? – LearnToday Aug 24 '18 at 04:09
  • The function is hosted in that context. You can simply code: `return getLocation(file);` – Ram Aug 24 '18 at 04:10
  • 1
    @undefined You can assume that `getLocation` and `fileFunction` are both methods of some outer object or class, so `this.getLocation` is correct. `getLocation(){}` by itself wouldn’t be valid syntax. – Sebastian Simon Aug 24 '18 at 04:11
  • Yeah, it is! thanks. @undefined I wanted the getLocation to run one after another! In async sequence – LearnToday Aug 24 '18 at 04:12
  • @Xufox Of course. Missing `function` keyword. Since there are many transpilers that does magic stuff, I have assumed the code somehow syntactically is valid, otherwise before the `TypeError`, a `SyntaxError` should be thrown. Maybe that's not the complete related code! – Ram Aug 24 '18 at 04:15
  • @Xufox thanks. How do I make sure the getLocation function completes in a async fashion? Such that, file at index 0 is downloaded and pushed before file at index 1....and so on? – LearnToday Aug 24 '18 at 04:23
  • @Xufox - Except that `this.getLocation(file)` isn't valid because `this` is not what the OP wants it to be - it's not the parent object because it's inside another callback function. Using an arrow function for `Promise.map()` would fix that part of the issue. – jfriend00 Aug 24 '18 at 05:28

1 Answers1

1

this.getLocation(file) does not work because you've lost the value of this because you're inside a Promise.map() callback. Remember, that every normal function call in Javascript changes the value of this unless you specifically control the value of this.

You can fix that part of the issue with a simple arrow function for your callback like this:

fileFunction(files){
  return Promise.map(files, file => {
     return this.getLocation(file); 
  }).then(locations => {
      // add these locations onto this.img_array
      this.img_array.push(...locations);
      console.log('done');
      return locations;
  });
}

This assumes that this.getLocation(file) returns a promise that resolves to the location value. Are you sure it does that? It looks like there may be more to your problem than just that first error you ran into.


And, after a side conversation, you also need to fix getLocation() to return a promise that resolves to the desired URL. Looking in the firebase Javascript doc, it appears that getDownloadURL() already returns a promise that resolves to the desired URL. So, you can just return that promise and let Promise.map() manage the results for you.

getLocation(file){
 if(file){
  return this._storage.ref(file).getDownloadURL();
 } else {
  return Promise.reject(new Error("No file passed to getLocation"));
 }
}

And, then you would use it all like this:

obj.fileFunction(fileArray).then(locations => {
    console.log(locations);
}).catch(err => {
    console.log(err);
});
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • This works, except that, I think the `this.getLocation(file)` isn't called in an async manner. The `this.img_array` array files index are not the same as in the `files` array. ```this.files=[0: "xngb", 1: "bdje"]``` but in the downloaded urls I get ```this.img_array=[0: "...bdje", 1: "...xngb"]```. How do I make sure the getLocation(file) functions works async? thanks – LearnToday Aug 24 '18 at 05:41
  • @ObasiObenyOj - What do you mean "how do I make sure the `getLocation(file)` is called async"? The implementation of `getLocation()` either is async or it's not. It isn't the caller that determines that. It's the implementation of `getLocation()` that determines that and what it calls and what it returns (it should be returning a promise that resolves with the desired value if it's async). – jfriend00 Aug 24 '18 at 05:44
  • @ObasiObenyOj - `Promise.map()` will maintain order of the items for you. Trying to push items into the array yourself with `this.img_array.push(url)` is a mistake. You should get the resolved values from `Promise.map()` and then they will be in the right order. – jfriend00 Aug 24 '18 at 05:47
  • ```getLocation(file){ return this._storage.ref(file).getDownloadURL().subscribe(url => // console.log(url) this.img_array.push(url) );}``` is my get location function. As I was trying to get the resulting ```this.img_array``` files index to match the index of the files in the ```files``` array. thanks – LearnToday Aug 24 '18 at 05:47
  • @ObasiObenyOj - Does `getLocation()` return a promise? I have no idea what `this._storage.ref(file).getDownloadURL().subscribe(...)` is so I can't look it up in any doc to learn more about it. – jfriend00 Aug 24 '18 at 05:48
  • it returns a firebase subscription...```return this._storage.ref(file).getDownloadURL().subscribe(url => // console.log(url) this.img_array.push(url) );``` – LearnToday Aug 24 '18 at 05:49
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/178661/discussion-between-obasi-obeny-oj-and-jfriend00). – LearnToday Aug 24 '18 at 05:49
  • If a firebase subscription isn't a promise, it won't work with `Promise.map()`. I don't know firebase so I can't really help you with that part. – jfriend00 Aug 24 '18 at 05:49
  • ``console.log(locations)`` returns observables, and what is the obj. property from? – LearnToday Aug 24 '18 at 07:01
  • @ObasiObenyOj - `obj` in `obj.fileFunction()` is whatever your object is that has the method `.fileFunction()`. – jfriend00 Aug 24 '18 at 07:23
  • @ObasiObenyOj - According to this [firebase doc](https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getDownloadURL). `_storage.ref().getDownloadURL()` returns a promise that resolves to the url. `Promise.map()` will wait for those promises to resolve and it's `.then()` handler will be passed an array of urls in proper order. For me to help you debug your fixed up code, you would have to show me exactly what code you're running by adding it to the end of your current question and formatting it properly. I can't debug code I can't see. I won't be awake much longer. – jfriend00 Aug 24 '18 at 07:26
  • Thanks! I added the code am using. the major change is the ``` locations.map((arr) => arr.subscribe((files) => this.downloadUrls.push(files)));``` so as to have an array of the files. – LearnToday Aug 24 '18 at 07:34
  • @ObasiObenyOj - I've taken this as far as I can tonight. I have no idea what you're trying to do with `arr.subscribe()`. `locations` should be an array of download URLs which I thought was the whole point here. – jfriend00 Aug 24 '18 at 07:40
  • Thnks, u did alot! I thought so. but it returns an array of observables. That's why I use the arr.subscribe. ```4) [Observable, Observable, Observable, Observable] 0:Observable {_isScalar: false, _subscribe: ƒ} 1:Observable {_isScalar: false, _subscribe: ƒ} 2:Observable {_isScalar: false, _subscribe: ƒ} 3:Observable {_isScalar: false, _subscribe: ƒ}``` – LearnToday Aug 24 '18 at 07:42