javascript - async/wait implícitamente devuelve promesa?
node.js async-await (3)
Leí que las funciones asíncronas marcadas por la palabra clave
async
implícitamente devuelven una promesa:
async function getVal(){
return await doSomethingAync();
}
var ret = getVal();
console.log(ret);
pero eso no es coherente ... suponiendo que
doSomethingAsync()
devuelve una promesa, y la palabra clave wait devolverá el valor de la promesa, no la promesa en sí misma, entonces mi función getVal
debería
devolver ese valor, no una promesa implícita.
Entonces, ¿cuál es exactamente el caso? ¿Las funciones marcadas por la palabra clave asíncrona devuelven implícitamente promesas o controlamos lo que devuelven?
¿Quizás si no devolvemos algo explícitamente, entonces implícitamente devuelven una promesa ...?
Para ser más claro, hay una diferencia entre lo anterior y
function doSomethingAync(charlie) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(charlie || ''yikes'');
}, 100);
})
}
async function getVal(){
var val = await doSomethingAync(); // val is not a promise
console.log(val); // logs ''yikes'' or whatever
return val; // but this returns a promise
}
var ret = getVal();
console.log(ret); //logs a promise
En mi sinopsis, el comportamiento es de hecho inconsistente con las declaraciones de retorno tradicionales.
Parece que cuando devuelve explícitamente un valor no prometido de una función
async
, forzará a envolverlo en una promesa.
No tengo un gran problema con él, pero desafía el JS normal.
Eché un vistazo a las especificaciones y encontré la siguiente información.
La versión corta es que una
async function
desugará a un generador que produce
Promise
s.
Entonces,
sí, las funciones asíncronas devuelven promesas
.
De acuerdo con la especificación tc39 , lo siguiente es cierto:
async function <name>?<argumentlist><body>
Desugar a:
function <name>?<argumentlist>{ return spawn(function*() <body>, this); }
Donde
spawn
"es una llamada al siguiente algoritmo":
function spawn(genF, self) {
return new Promise(function(resolve, reject) {
var gen = genF.call(self);
function step(nextF) {
var next;
try {
next = nextF();
} catch(e) {
// finished with failure, reject the promise
reject(e);
return;
}
if(next.done) {
// finished with success, resolve the promise
resolve(next.value);
return;
}
// not finished, chain off the yielded promise and `step` again
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
El valor de retorno siempre será una promesa. Si no devuelve una promesa explícitamente, el valor que devuelva se incluirá automáticamente en una promesa.
async function increment(num) {
return num + 1;
}
// Even though you returned a number, the value is
// automatically wrapped in a promise, so we call
// `then` on it to access the returned value.
//
// Logs: 4
increment(3).then(num => console.log(num));
Lo mismo, incluso si hay una
await
.
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}
async function incrementTwice(num) {
const numPlus1 = await defer(() => num + 1);
return numPlus1 + 1;
}
// Logs: 5
incrementTwice(3).then(num => console.log(num));
Promesas de desempaquetado automático, por lo que si devuelve una promesa por un valor desde una función
async
, recibirá una promesa por el valor (no una promesa por una promesa por el valor).
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}
async function increment(num) {
// It doesn''t matter whether you put an `await` here.
return defer(() => num + 1);
}
// Logs: 4
increment(3).then(num => console.log(num));
En mi sinopsis, el comportamiento es de hecho inconsistente con las declaraciones de retorno tradicionales. Parece que cuando devuelve explícitamente un valor no prometido de una función asíncrona, forzará a envolverlo en una promesa. No tengo un gran problema con él, pero desafía el JS normal.
ES6 tiene funciones que no devuelven exactamente el mismo valor que el
return
.
Estas funciones se llaman generadores.
function* foo() {
return ''test'';
}
// Logs an object.
console.log(foo());
// Logs ''test''.
console.log(foo().next().value);
async no devuelve la promesa, la palabra clave esperar aguarda la resolución de la promesa. async es una función de generador mejorada y espera funciona un poco como el rendimiento
Creo que la sintaxis (no estoy 100% seguro) es
async function* getVal() {...}
Las funciones del generador ES2016 funcionan un poco así. He hecho un manejador de base de datos basado en tedioso que programa como este
db.exec(function*(connection) {
if (params.passwd1 === '''') {
let sql = ''UPDATE People SET UserName = @username WHERE ClinicianID = @clinicianid'';
let request = connection.request(sql);
request.addParameter(''username'',db.TYPES.VarChar,params.username);
request.addParameter(''clinicianid'',db.TYPES.Int,uid);
yield connection.execSql();
} else {
if (!/^/S{4,}$/.test(params.passwd1)) {
response.end(JSON.stringify(
{status: false, passwd1: false,passwd2: true}
));
return;
}
let request = connection.request(''SetPassword'');
request.addParameter(''userID'',db.TYPES.Int,uid);
request.addParameter(''username'',db.TYPES.NVarChar,params.username);
request.addParameter(''password'',db.TYPES.VarChar,params.passwd1);
yield connection.callProcedure();
}
response.end(JSON.stringify({status: true}));
}).catch(err => {
logger(''database'',err.message);
response.end(JSON.stringify({status: false,passwd1: false,passwd2: false}));
});
Observe cómo lo programo como síncrono normal, particularmente en
yield connection.execSql
y en
yield connection.callProcedure
La función db.exec es un generador bastante típico basado en Promise
exec(generator) {
var self = this;
var it;
return new Promise((accept,reject) => {
var myConnection;
var onResult = lastPromiseResult => {
var obj = it.next(lastPromiseResult);
if (!obj.done) {
obj.value.then(onResult,reject);
} else {
if (myConnection) {
myConnection.release();
}
accept(obj.value);
}
};
self._connection().then(connection => {
myConnection = connection;
it = generator(connection); //This passes it into the generator
onResult(); //starts the generator
}).catch(error => {
reject(error);
});
});
}