Me encantan los cursos que son una implementación naive de algo para entenderlo mejor.
- git
- lisp interpreter
- yarn
- blockchain
- vasco: service discovery in 100 lines of js https://github.com/asyncanup/vasco/blob/33b878b3ae43f2c75197ea3e93afa0876ce990de/vasco-talk.pdf
readFile('file.json', (err, data) => console.log(data))
readFile(filename, cb) {
const data = "MarsBased";
cb(undefined, data);
}
fetch("file.json").then(data => console.log(data));
function fetch(filename) {
const data = "MarsBased";
return {
then(cb) {
return Promise(cb(data));
}
};
}
fetch("file.json")
.then(data => data.toLowerCase())
.then(data => console.log(data));
function fetch(filename) {
return Promise("VenusBased");
}
function Promise(data) {
return {
then(cb) {
return Promise(cb(data));
}
};
}
Fisrt attempt: fails because it breaks the chain
function fetch(filename) {
return filename
? Promise("MarsBased")
: Promise(null, "`filename` is required");
}
function Promise(data, err) {
return {
then(cb) {
if (!err) return Promise(cb(data));
},
catch(cb) {
if (err) cb(err);
}
};
}
fetch("file.json")
.then(data => data.toLowerCase())
.then(data => console.log(data))
.catch(err => console.log(err));
fetch()
.then(data => data.toLowerCase())
.then(data => console.log(data))
.catch(err => console.log(err));
marsbased
/Users/Dani/MarsBased/Promises/index.js:25
.then(data => console.log(data))
^
TypeError: Cannot read property 'then' of undefined
fetch("file.json")
.then(data => data.toLowerCase())
.then(data => console.log(data))
.catch(err => console.log(err));
function fetch(filename) {
return Promise("MarsBased", "A Big Error");
}
function Promise(data, err) {
return {
then(cb) {
if (err) return Promise(null, err);
else return Promise(cb(data));
},
catch(cb) {
if (err) cb(err);
}
};
}
const promise = new Promise((resolve, reject) => reject("Explode"));
promise
.then(data => console.log("success", data))
.catch(err => {
console.log("error", err);
return err.toLowerCase();
})
.catch(err => console.log("caught 2", err));
fetch("file.json")
.then(data => Promesa(data.toLowerCase()))
.then(data => console.log(data))
.catch(err => console.log(err));
function fetch(filename) {
return filename
? Promesa("MarsBased")
: Promesa(undefined, "`filename` is required");
}
function Promesa(data, err) {
return {
then(cb) {
if (err) {
return Promesa(undefined, err);
} else {
const result = cb(data);
return result && typeof result.then === "function"
? result
: Promesa(result);
}
},
catch(cb) {
if (err) cb(err);
}
};
}
fetch("file.json")
.then(data => {
throw "Invalid data";
})
.then(data => data.toLowerCase())
.then(data => console.log(data))
.catch(err => console.log(err));
function fetch(filename) {
return filename
? Promesa("MarsBased")
: Promesa(undefined, "`filename` is required");
}
function Promesa(data, err) {
return {
then(cb) {
if (err) {
return Promesa(undefined, err);
} else {
try {
const result = cb(data);
return result && typeof result.then === "function"
? result
: Promesa(result);
} catch (err) {
return Promesa(undefined, err);
}
}
},
catch(cb) {
if (err) cb(err);
}
};
}
fetch("file.json")
.then(data => {
throw "Invalid data";
})
.then(data => data.toLowerCase())
.then(data => console.log(data))
.catch(err => {
console.log("error", err);
throw "Invalid error handler";
})
.catch(err => console.log("error2", err));
function fetch(filename) {
return filename
? Promesa("MarsBased")
: Promesa(undefined, "`filename` is required");
}
function Promesa(data, err) {
return {
then(cb) {
if (err) {
return Promesa(undefined, err);
} else {
try {
const result = cb(data);
return result && typeof result.then === "function"
? result
: Promesa(result);
} catch (err) {
return Promesa(undefined, err);
}
}
},
catch(cb) {
try {
if (err) cb(err);
} catch (exception) {
return Promesa(undefined, exception);
}
}
};
}
fetch("file.json")
.then(data => data.toLowerCase())
.then(data => console.log(data))
.then(data => {
throw "Not valid";
})
.catch(err => console.log("error:", err));
// Internal API change, no behaviour
function fetch(filename) {
return filename
? Promesa((resolve, reject) => resolve("MarsBased"))
: Promesa((resolve, reject) => reject("`filename` is required"));
}
// First thing to do
Promesa.resolve = value => Promesa((resolve, reject) => resolve(value));
Promesa.reject = err => Promesa((resolve, reject) => reject(err));
function fetch(filename) {
return filename
? Promesa.resolve("MarsBased")
: Promesa.reject("`filename` is required");
// Internal and API change. No behaviour change.
function Promesa(executor) {
let data, err;
const resolve = value => (data = value);
const reject = value => (err = value);
executor(resolve, reject);
return {
then(cb) {
if (err) {
return Promesa.reject(err);
} else {
try {
const result = cb(data);
return result && typeof result.then === "function"
? result
: Promesa.resolve(result);
} catch (err) {
return Promesa.reject(err);
}
}
},
catch(cb) {
try {
if (err) cb(err);
} catch (exception) {
return Promesa.resolve(exception);
}
}
};
}
Nuestra implementación tiene un defecto sutil. Si ejecutamos el siguiente código:
function fetch(filename) {
return filename
? Promesa.resolve("MarsBased")
: Promesa.reject("`filename` is required");
}
fetch("file.json").then(data => console.log(data));
console.log("hello");
Obtendremos:
$ node steps/step-6.js
marsbased
hello
Pero lo normal, si fetch utilizase Promises (las verdaderas) obtendríamos ésto:
$ node index.js
marsbased
hello
Con esta nueva sintáxis nos permite escribir un fetch
asíncrono, con ayuda de setTimeout:
function fetch(filename) {
return filename
? Promesa(resolve => setTimeout(() => resolve("DATA"), 1000))
: Promesa.reject("`filename` is required");
}
fetch("file.json").then(data => console.log(data));
console.log("hello");
El problema es que, por desgracia, no acaba de funcionar:
$ time node index.js
hello
error: TypeError: Cannot read property 'toLowerCase' of undefined
at fetch.then.data
node index.js 0.06s user 0.02s system 7% cpu 1.084 total
Pero como se puede ver hay un avance: hello se ha imprimido antes que el error. Eso significa que cosole.log('hello')
se ha evaluado antes que nuestro código y por lo tanto nuestra promesa ha pasado a ser asíncrona. Eso lo corrobora el tiempo que tarda en total el programa en ejecutarse, ejem, en fallar: 1 segundo.
El problema es que cuando la función then
de la Promesa ejecuta el callback, el valor de data
aún no está definido (porque resolve
no se ha ejecutado).