Is there a way for the handler inside .then() method to access the newly created Promise object? - es6-promise

I have a chaining promise like this:
doSomething1().then(doSomething2).then(doSomething3) ...
Now I'd like to put a marker on each Promise object returned by .then() method for later code analysis. doSomething as an anonymous function, whose context is by default the window object. Is there a way to set the handler's context this pointing to the newly created Promise object?
Also, is there a way to list all the immediate then() attached to a particular Promise object?

You can define the functions to be called withing an object and use arrow function as executor passed to Promise constructor to preserve this referencing the context of the object where function is defined.
let promises = {
marker: "I'm before the first step Promise!",
doSomething1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
this.marker = "I'm the first step Promise!";
console.log(this.marker);
resolve(this);
}, Math.floor(Math.random() * 900))
})
},
doSomething2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
this.marker = "I'm the second step Promise!";
console.log(this.marker);
resolve(this);
}, Math.floor(Math.random() * 1200))
})
},
doSomething3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
this.marker = "I'm the third step Promise!";
console.log(this.marker);
resolve(this);
}, Math.floor(Math.random() * 1500))
})
}
}
console.log(promises.marker);
promises.doSomething1()
.then(promises.doSomething2)
.then(promises.doSomething3)

Related

Javascript - Return json from fetch in an Object

I'm trying to make an application to get the recipes from https://edamam.com and I'm using fetch and Request object.
I need to make 3 request, and i thought that most beautiful way for do it is make an Object and a method that return the data in JSON.
I declarated into constructor a variable called this.dataJson, and i want to save there the data in JSON from the response. For that purpose i use this.
The problem is that i have a undefined variable.
.then( data => {this.dataJson=data;
console.log(data)} )
This is all my code.
class Recipe{
constructor(url){
this.url=url;
this.dataJson;
this.response;
}
getJson(){
var obj;
fetch(new Request(this.url,{method: 'GET'}))
.then( response => response.json())
.then( data => {this.dataJson=data;
console.log(data)} )
.catch( e => console.error( 'Something went wrong' ) );
}
getData(){
console.log("NO UNDFEIND"+this.dataJson);
}
}
const pa= new Recipe('https://api.edamam.com/search?...');
pa.getJson();
pa.getData();
I'm new studying OOP in JS and more new in Fetch requests...
If you guys can help me... Thanks very much!
Here's a solution using async-await (and a placeholder API):
class Recipe {
constructor(url) {
this.url = url;
this.dataJson;
this.response;
}
// the async keyword ensures that this function returns
// a Promise object -> we can use .then() later (1)
async getJson() {
try {
const response = await fetch(new Request(this.url, {
method: 'GET'
}))
const json = await response.json()
this.dataJson = json
} catch (e) {
console.error('Something went wrong', e)
}
}
getData() {
console.log("NO UNDFEIND:", this.dataJson);
}
}
const pa = new Recipe('https://jsonplaceholder.typicode.com/todos/1');
// 1 - here we can use the "then", as pa.getJson() returns
// a Promise object
pa.getJson()
.then(() => {
pa.getData()
});
If we want to stay closer to your code, then:
class Recipe {
constructor(url) {
this.url = url;
this.dataJson;
this.response;
}
getJson() {
// var obj; // not needed
// the "fetch" always returns a Promise object
return fetch(new Request(this.url, { // return the fetch!
method: 'GET'
}))
.then(response => response.json())
.then(data => {
this.dataJson = data;
// console.log(data) // not needed
})
.catch(e => console.error('Something went wrong'));
}
getData() {
console.log("NO UNDFEIND:", this.dataJson); // different syntax here
}
}
const pa = new Recipe('https://jsonplaceholder.typicode.com/todos/1');
// using "then", because the "fetch" returned a Promise object
pa.getJson()
.then(() => {
pa.getData();
});
The problem with your original code is that you initiate the request (pa.getJson()) and then immediately (on the next line) you want to read the data (pa.getData()). pa.getData() is called synchronously (so it happens in milliseconds), but the request is asynchronous - the data needs time to arrive (probably hundreds of milliseconds) - so, it's not there when you try to read it (it simply hasn't arrived yet).
To avoid this you have to use a technique to handle this asynchronous nature of the request:
use a callback function (blee - so last decade)
use a Promise object with then() (much better) or async-await (yeee!)
and call the pa.getData() when the response has arrived (inside the callback function, in the then() or after awaiting the result).

why promise.resolve lost value inside of async function?

I know when we use promise in JavaScript, we usally use two ways like below.
var f1 = () => {
return new Promise( (resolve, reject) => {
resolve(10);
})
}
var f2 = () => {
return Promise.resolve(10)
}
f1().then( data => { console.log(data) }) // 10
f2().then( data => { console.log(data) }) // 10
But if i use async function inside of promise, Promise.resolve lost the value like below.
const fs = require('fs')
var f1 = () => {
return new Promise( (resolve, reject) => {
fs.readFile('data.txt', (err, data) => {
resolve(data);
})
})
}
var f2 = () => {
return Promise.resolve()
.then(() => {
fs.readFile('data.txt', (err, data) => {
//return data --> undefined
//Promise.resolve(data) --> undefined
return Promise.resolve() --> undefined
})
})
}
f1().then( data => { console.log('1,',data) })
f2().then( data => { console.log('2,',data) })
I think that i use wrong way about Promise.resolve,,, OR Promise.resolve not support async function... someone tell me why Promose.resolve fail..
If you have a value that you immediately want to resolve a promise with, you can use Promise.resolve().
If you get the value asynchronously in a callback, you must use the new Promise constructor with the resolve/reject callbacks, there is no way around it.

wrapping promise+then in new promise

I have a simple promise:
function p(l) {
return new Promise(function(resolve) {
alert(l);
resolve();
});
}
I then try to create a promise that will only resolve after it's then clause:
function pp(l1, l2) {
return new Promise(function(resolve) {
p(l1).then(() => {
alert(l2);
resolve();
});
});
}
I now do:
p('start').then(pp('A', 'B')).then(p('end'));
I expected to see: start, A, B, end
but instead I see: start, A, end, B
why ?
how can I achieve what I want, creating a promise (pp) that will resolve when it's then clause finished ?
pp can be coded more simply:
function pp(l1, l2) {
return p(l1).then(() => {
alert(l2);
});
}
This is the whole point of promises, if you have to write complicated code everytime, it's not worth it :)
Also, .then expects a function callback with in parameter the return value of the previous promise/function.
p('start').then(() => pp('A', 'B')).then(() => p('end'));
As for the precise reason you had your behavior, outside of not using the .then chaining as it's meant to be, I'm not very clear why.
And the reason this code works, is because when returning a Promise inside a callback, that Promise is executed, and the value it resolves to is passed to the next callback as an argument. () => pp('A', 'B') being equivalent to () => { return pp('A', 'B');}, pp is returned and waited upon before calling the next callback of the chain.
If you want to keep the same .then calling syntax, then changing pp to return a callback instead of a Promise would work:
function pp(l1, l2) {
return () => p(l1).then(() => {
alert(l2);
});
}
But then you can no longer use it as a Promise. The last part of the chaining still needs to change: .then(() => p('end')).
In your second function try following
function pp(l1, l2) {
return new Promise(function(resolve) {
alert(l1);
alert(l2);
resolve();
});
}
Or if you want to put p function inside pp then
function pp(l1, l2) {
return p(l1).then(() => {
alert(l2);
});
}
p('start').then(() => pp('A', 'B')).then(() => p('end'));
Here is Demo https://plnkr.co/edit/fUisfcWrTu89xcc4j0cu?p=preview
from mdn - "onFulfilled: A Function called when the Promise is fulfilled. This function has one argument, the fulfillment value."
the problem with my code was that in then(pp('A', 'B')) I passed then a value (the result of pp() which is a promise) and not a function (a function that receives the fulfillment value of the calling promise)
So the correct way to use then is to always pass it a function:
function p(l) {
return new Promise(function(resolve) {
alert(l);
resolve();
})
}
function pp(l1, l2) {
return p(l1).then(() => p(l2));
}
p('start').then(() => pp('A', 'B')).then(() => p('end'));

(Serverless Framework Module) wait for promise to resolve before return statement

Is it possible for a Serverless Framework module to wait for the "resolve" of a promise before returning?
I'm aware that promises themselves can't do that, but different frameworks/libraries (express, Jasmine, hapijs, etc.) solve this by having a method that defines when to return. I need something like this:
let http = require('http'),
Promise = require('bluebird');
let action = (done) => {
return new Promise((resolve, reject) => {
http
.get('http://domain.com', resolve.bind({}, 'all good!'))
.on('error', reject.bind({}, 'all wrong!'));
})
.then((response) => {
console.log('Result', response);
return done(response); // <----------- I wan't to see this as the response
// of the lambda function
});
};
module.exports.run = (event, context, cb) => cb(null, action(done));
No, promises don't do that. It's impossible to read from the future, and don't want to (cannot) block. Your action is still asynchronous.
But given that your export takes a callback anyway, you can simply invoke that asynchronously:
module.exports.run = (event, context, cb) => {
action().then(res => cb(null, res), err=>cb(err));
};
It would be better though of course if you just returned the promise.

What's the easiest way to bluebird promisify a function with its callback as the second-to-last value?

I'm trying to promisify this geocoder library for connecting to an external api. The readme says that its geocoder.geocode method takes location, callback as arguments, but on closer inspection, it actually takes a third argument — an options object — so it can't be easily promisified with bluebird's Promise.promisify().
What's the most quick & simple way to promisify these sorts of library methods?
The following works, but is there an easier way?
function geocoderAsync(string) {
return new Promise(function (resolve, reject) {
return geocoder.geocode(string, function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
I would probably do something like this if that's the only odd function in the library. And I think Bluebird.fromCallback is the function you're looking for.
var Promise = require('bluebird'),
geocoder = Promise.promisifyAll(require('geocoder'));
// If that is the only function with the odd callback order
// you could simply re-overwrite the `geocode` function.
geocoder.geocodeAsync = function (string, options) {
return Promise.fromCallback(function (callback) {
geocoder.geocode(string, callback, options);
});
};