"A little help!" JavaScript 中的异步语法。我是一个老新手,我发现这有点令人沮丧



我只想运行一个异步函数,让它等待它有一个返回值,将该返回值传递给另一个异步功能,然后清洗并重复几次。我看到了一些例子,但我正在努力使它们在测试代码中发挥作用。请原谅所有的棒球背景,但我不得不为自己造成的鼻出血找点乐子。

如果不在附加的代码中传递返回值,我甚至无法使其工作,现在感觉很愚蠢。。。

whosOnFirst(1)
.then(whatsOnSecond(2))
.then(iDunnosOnthird(3)) 

我想看看执行whosOnFirst(1)的语法,将返回值传递给whatsOnSecond(),并将其返回值传递到iDunnosOnthird()。创建代码段底部引用的所需输出(减去空格)。

我真的很感激你的帮助!

// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { // 
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
return pause + 1; // delayed return value / adds 1 to input param 
}, pause * 1000);
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
waitSec("who's on first", 1).then(result => {
return result;
})
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
waitSec("what's on second", i).then(result => {
return result;
})
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
waitSec("iDunno's on third", i).then(result => {
return result;
})
}
whosOnFirst(1)
.then(whatsOnSecond(2))
.then(iDunnosOnthird(3))
// CURRENT OUTPUT: 
// who's on first called, waiting 1 second(s)...
// what's on second called, waiting 2 second(s)...
// iDunno's on third called, waiting 3 second(s)...
// who's on first sent 2 in a return, and done!
// what's on second sent 3 in a return, and done!
// iDunno's on third sent 4 in a return, and done!
// DESIRED OUTPUT:
// who's on first called, waiting 1 second(s)...
// who's on first sent 2 in a return, and done!
// what's on second called, waiting 2 second(s)...
// what's on second sent 3 in a return, and done!
// iDunno's on third called, waiting 3 second(s)...
// iDunno's on third sent 4 in a return, and done!

工作解决方案

您的问题是waitSec与代码的其余部分处于不同的上下文中。您需要将其提升到Promise上下文中。

然后,您就可以一直"promised"代码,并且可以将promises链接起来。它在这里工作(下面会有进一步的解释):

// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { // 
return new Promise(resolve => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param 
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
function whosOnFirst(i) {
return waitSec("who's on first", 1)
}
// What's on Second - run after completion of whosOnFirst() using return for param
function whatsOnSecond(i) {
return waitSec("what's on second", i)
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
function iDunnosOnthird(i) {
return waitSec("iDunno's on third", i)
}
whosOnFirst(1)
.then(whatsOnSecond)
.then(iDunnosOnthird)
.then(console.log)

进一步解释

waitSec的原始实现总是将undefined返回给调用者:

// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { // 
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
return pause + 1; // delayed return value / adds 1 to input param 
}, pause * 1000);
// return undefined happens here
}

setTimeout内部的返回不会返回到您的调用代码,因为您的代码从不调用此函数。它由JS定时器代码异步调用。

使用回调从异步代码返回值

将结果传递给调用者的方法是将回调作为外部函数的参数,然后在异步函数中调用该回调,如下所示:

function waitSec({callerName, pause, callback}) {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
callback(pause + 1); // delayed return value / adds 1 to input param 
}, pause * 1000);
}

在这种情况下,您有JS的经典回调模式。

使用承诺,以及您可能更喜欢它们的原因

或者,您返回一个Promise,并解析它以返回值——正如我在解决方案中演示的那样。有关这方面的更多信息,请搜索"我如何承诺回调"。

这样做(使用Promise)的好处是Promise是可组合的,如解决方案中所示。

Promise也可以使用await,因此可以使用带有Promise返回函数的async/await

您不需要将Promise返回函数标记为async

如果将函数标记为async,它将返回Promise,如下所示:

// A function marked async returns a Promise
async function simple() {
return 3
}
// Proof that it is a Promise 
simple().then(console.log)
// The equivalent code explicitly returning a Promise - this is what `async` does
function simpleP(n = 0) {
return new Promise(resolve => resolve(n+1))
}
// Proof that it behaves the same
simpleP().then(console.log)
// Promise composition
simple()
.then(simpleP)
.then(console.log)

// Same functionality, using async/await
async function main() {
const num = await simple()
const res = await simpleP(num)
console.log(`${num} + 1 = ${res}`)
}
main()

标记同步返回函数async使它们可以与实际需要Promise来返回值的异步代码组合。您可以使用它将函数提升到相同的异步上下文中并组合它们。

// Synchronous function that is Promisified and composable
async function simple() {
return 3
}

要从接受回调并通过回调提供返回的异步函数中实际获取值,必须从函数中构造并返回Promise,然后在提供要通信的值的回调中调用Promiseresolve

function getDBRecordP(id) {
return new Promise((resolve, reject) => 
dbLib.get(id, 
(err, result) => err ? 
reject(err) : 
resolve(result)))
}

在这种情况下,您必须明确地连接Promise机制。这几乎是您需要创建并返回Promise的唯一时间。

它所做的是为您包装一个回调接口。

function getDBRecordP(id) {
return new Promise((resolve, reject) => 
dbLib.get(id, 
(err, result) => err ? 
reject(err) : 
resolve(result)))
}

请注意,在回调示例中,您必须将调用方的callback参数作为参数,然后在提供该值的回调中使用返回值来调用该参数。

使用Promise机制,您确实接受了一个回调参数,但它不是由调用者提供的,而是由您构造的Promise提供的。然后您将Promise返回给呼叫者。

上面的getDBRecordP函数是"如何将接受回调的函数转换为返回Promise的函数?"的精髓。

然后,调用者将回调传递给Promise.then()方法。

因此,您有一台机器,它将回调传递约定封装到可组合的正式API中,而回调则不是。

Promise机器的另一个方面是.catch()方法。

对于回调,惯例一直是回调签名为(err, result),而Promise机器允许您提供两个回调,一个用于err,一个为result

请使用对async函数来说很漂亮的await关键字。你的waitSec函数应该返回一个promise,当你解析promise时,你的值出现在.then((res)=> console.log(res))中,使用它进行如下链接:

// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { // 
return new Promise((resolve, reject) => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function() {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param 
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
const resp = await waitSec("who's on first", 1);
return resp;
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
const resp = await waitSec("what's on second", i);
return resp;
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
const resp = await waitSec("iDunno's on third", i);
return resp;
}
whosOnFirst(1).then((firstResp) => {
whatsOnSecond(firstResp).then((secResp) => {
iDunnosOnthird(secResp).then((thirdResp) => {
console.log(thirdResp);
})
})
})

您也可以将链中的值与async/await:一起使用,如下所示

// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { // 
return new Promise((resolve, reject) => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function() {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param 
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
const resp = await waitSec("who's on first", 1);
return resp;
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
const resp = await waitSec("what's on second", i);
return resp;
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
const resp = await waitSec("iDunno's on third", i);
return resp;
}
let test = async() => {
var res1 = await whosOnFirst(1);
var res2 = await whatsOnSecond(res1);
var res3 = await iDunnosOnthird(res2);
console.log(res3);
}
test();

您需要等待每个结果,如下所示:

async function main() {
const result1 = await whosOnFirst(1);
// whosOnSecond will not get called except after the whosOnFirst is done.
const result2 = await whosOnSecond(result1);
}

以下是它的工作原理:

function delay(milliseconds){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
}, milliseconds);
});
}
async function test(){
console.log('no delay, yet');
await delay(1000);
console.log('After 1 second');
await delay(1000);
console.log('2 seconds have passed');
await delay(3000);
console.log('Oh look, 5 seconds have passed!');
}
test();

async函数await,直到Promiseresolved,然后转到Promise之后的下一个代码块,可能是awaiting。。。再一次。我承认我觉得奇怪的是,async函数真的同步运行await,但async函数本身是异步的。。。意味着紧接在执行的CCD_ 33之后执行的另一个函数可能在CCD_。

关于你的例子:

class Counter{
constructor(increment = 1, initially = 0){
this.increment = increment; this.count = initially;
}
inc(){
return this.count += this.increment;
}
dec(){
return this.count -= this.increment;
}
}
function delay(milliseconds){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
}, milliseconds);
});
}
const sec = new Counter;
function logCaller(callerName){
console.log(`${callerName} called, after ${sec.inc()} second(s)...`);
}
async function what(){
await delay(1000);
logCaller("who's on first");
await delay(1000);
logCaller("what's on second");
await delay(1000);
logCaller("iDunno's on third");
}
what();

相关内容

最新更新