Async/await неявно возвращает обещание?
Я прочитал, что асинхронные функции, отмеченные ключевым словом async
, неявно возвращают обещание:
async function getVal(){
return await doSomethingAync();
}
var ret = getVal();
console.log(ret);
но это не является когерентным... если doSomethingAsync()
возвращает обещание, а ключевое слово ожидания будет возвращать значение из обещания, а не обещание itsef, тогда моя функция getVal должна вернуть это значение, а не подразумеваемое обещание.
Итак, что именно происходит? Функции, отмеченные ключевым словом async, неявно возвращают promises или мы контролируем, что они возвращают?
Возможно, если мы явно не вернем что-то, то они неявно вернут обещание...?
Чтобы быть более ясным, существует разница между приведенным выше и
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
В моем резюме поведение действительно противоречит традиционным операторам return. Похоже, что когда вы явно возвращаете неопределенное значение из функции async
, это заставит его обернуть в обещание.
У меня нет большой проблемы с этим, но он бросает вызов нормальному JS.
Ответы
Ответ 1
Возвращаемое значение всегда будет обещанием. Если вы явно не вернете обещание, возвращаемое вами значение автоматически будет завернуто в обещание.
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));
То же самое, даже если есть 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));
Promises auto-unwrap, поэтому, если вы вернете обещание для значения из функции async
, вы получите обещание для значения (не обещание для обещания для значения).
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));
В моем резюме поведение действительно противоречит традиционным возвращения. Похоже, что когда вы явно возвращаете значение non-prom от функции async, это заставит ее обернуть Обещаю. У меня нет большой проблемы с этим, но он бросает вызов нормальному JS.
ES6 имеет функции, которые не возвращают точно такое же значение, как return
. Эти функции называются генераторами.
function* foo() {
return 'test';
}
// Logs an object.
console.log(foo());
// Logs 'test'.
console.log(foo().next().value);
Ответ 2
Я посмотрел на спецификации и нашел следующую информацию. Короткая версия состоит в том, что async function
десугарсует к генератору, который дает Promise
s. Итак, да, асинхронные функции возвращают обещания.
Согласно спецификации tc39, верно следующее:
async function <name>?<argumentlist><body>
Desugars для:
function <name>?<argumentlist>{ return spawn(function*() <body>, this); }
Где spawn
"это вызов следующего алгоритма":
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); });
});
}
Ответ 3
async не возвращает обещание, ключевое слово await ожидает разрешения обещания. async - это расширенная функция генератора, и она работает немного как выход
Я думаю, что синтаксис (я не уверен на 100%)
async function* getVal() {...}
Функции генератора ES2016 работают примерно так. Я сделал обработчик базы данных, основанный на вершине утомительного, который вы программируете как этот
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}));
});
Обратите внимание, как я просто программирую его как обычный синхронный, особенно в
yield connection.execSql
и при yield connection.callProcedure
Функция db.exec является довольно типичным генератором на основе 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);
});
});
}