Javascript 如何延迟代码的执行,直到fetch返回值

Javascript 如何延迟代码的执行,直到fetch返回值,javascript,fetch,Javascript,Fetch,我创建了以下两个函数来从后端获取JWT令牌,并将其存储在localStorage中。逻辑看起来很简单,但不起作用,因为在解析令牌的值之前,代码会继续执行。因此,链中需要令牌的函数无法获得该令牌。最终,它确实获得了令牌并将其存储在localStorage中,但到那时,启动链中的所有函数都无法获得有效的令牌 当我的React应用程序启动时,我一开始就在做所有这些。因此,我们的想法是获取令牌,然后执行其余的启动步骤 因此,我在我的应用程序的入口点调用myStartup()函数。我这样做是第一步。从那里

我创建了以下两个函数来从后端获取
JWT令牌
,并将其存储在
localStorage
中。逻辑看起来很简单,但不起作用,因为在解析令牌的值之前,代码会继续执行。因此,链中需要令牌的函数无法获得该令牌。最终,它确实获得了令牌并将其存储在
localStorage
中,但到那时,启动链中的所有函数都无法获得有效的令牌

当我的React应用程序启动时,我一开始就在做所有这些。因此,我们的想法是获取令牌,然后执行其余的启动步骤

因此,我在我的应用程序的入口点调用
myStartup()
函数。我这样做是第一步。从那里,剩余的启动函数作为回调传递

export const getJwtTokenFromApi = () => {

    var request = new Request('/api/token', {
        method: 'GET',
        mode: 'cors',
        credentials: 'include'
    });

    fetch(request)
        .then((response) => {
            response.text()
            .then((token) => {
                if(token.length > 0) {
                    localStorage.setItem('myToken', token);
                    return token;
                } else {
                    return null;
                }
             })
        })
        .catch(err => {
        });
}

export const getJwtToken = () => {

    let token = localStorage.getItem('myToken');

    if (token == null)
        token = getJwtTokenFromApi();

    return token;
}

export const myStartup = (callback) => {

    const token = getJwtToken();
    callback(token);
}
最终发生的情况是,需要令牌的调用函数不会延迟到令牌被接收时,因此它最终会接收
未定义的


如何确保延迟执行需要令牌的函数,直到我拥有合法令牌——或者至少有一个表示API调用失败的空值?

您的函数
getJwtToken
应该返回承诺:

export const getJwtToken = () => {   
  let token = localStorage.getItem('myToken');
  return token ? Promise.resolve(token) : getJwtTokenFromApi(storeToken) 
}
在您的呼叫者中,令牌将包装在内部返回的承诺中:

getJwtToken().then(token => doSomething(token))

您的函数
getJwtToken
应该返回承诺:

export const getJwtToken = () => {   
  let token = localStorage.getItem('myToken');
  return token ? Promise.resolve(token) : getJwtTokenFromApi(storeToken) 
}
在您的呼叫者中,令牌将包装在内部返回的承诺中:

getJwtToken().then(token => doSomething(token))

我用下面的代码实现了这一点,但我认为它不是很优雅

本质上,我将多个函数合并为一个,并添加了一个回调参数,以便创建一个启动函数链。如果没有收到回调,我只返回令牌,以使
getJwtToken()
成为一个多用途函数,即调用它来获取令牌或传递期望令牌的函数

我真的希望有单独的功能,以便不是所有的关注点都在一个功能中。另外,当我需要只获取令牌时,不要疯狂地使用回调参数

我想发布代码,这样我可以得到一些建议,使它更加健壮和优雅

export const getJwtToken = (callback) => {

    // If token is already in localStorage, get it and return it.
    const token = localStorage.getItem('myToken');
    if (token != null)
        return token;

    // Token is not in localStorage. Get it from API
    var request = new Request('/api/token', {
        method: 'GET',
        mode: 'cors',
        credentials: 'include'
    });

    fetch(request)
        .then((response) => {

            response.text()
                .then((token) => {

                    if (token.length > 0) {

                        // First, save it in localStorage
                        localStorage.setItem('myToken', token);

                        // If no callback function received, just return token
                        if (typeof callback == "undefined") {

                            return token;
                        } else {

                            callback(token);
                        }
                    }
                })
        })
        .catch(err => {
        });
}

我用下面的代码实现了这一点,但我认为它不是很优雅

本质上,我将多个函数合并为一个,并添加了一个回调参数,以便创建一个启动函数链。如果没有收到回调,我只返回令牌,以使
getJwtToken()
成为一个多用途函数,即调用它来获取令牌或传递期望令牌的函数

我真的希望有单独的功能,以便不是所有的关注点都在一个功能中。另外,当我需要只获取令牌时,不要疯狂地使用回调参数

我想发布代码,这样我可以得到一些建议,使它更加健壮和优雅

export const getJwtToken = (callback) => {

    // If token is already in localStorage, get it and return it.
    const token = localStorage.getItem('myToken');
    if (token != null)
        return token;

    // Token is not in localStorage. Get it from API
    var request = new Request('/api/token', {
        method: 'GET',
        mode: 'cors',
        credentials: 'include'
    });

    fetch(request)
        .then((response) => {

            response.text()
                .then((token) => {

                    if (token.length > 0) {

                        // First, save it in localStorage
                        localStorage.setItem('myToken', token);

                        // If no callback function received, just return token
                        if (typeof callback == "undefined") {

                            return token;
                        } else {

                            callback(token);
                        }
                    }
                })
        })
        .catch(err => {
        });
}

根本的问题是,您没有像处理异步内容一样处理所有异步内容。这是一个相当复杂的工作流,阻塞和非阻塞任务混杂在一起,这意味着我们需要在整个过程中应用异步模式。让我们一步一步地完成脚本中的一个函数


这似乎是脚本的入口点:

export const myStartup = (callback) => {
    const token = getJwtToken();
    callback(token);
}
它不会工作,因为
getJwtToken
是异步的,这意味着它的值将不可用于下一行的
callback

我们如何知道
getJwtToken
是异步的?因为
getJwtToken
调用
getJwtTokenFromApi
,调用
fetch
(规范告诉我们是异步的)。因为
getJwtToken
包装了异步行为,所以它本身是异步的

由于
getJwtToken
是异步的,我们知道
token
callback
需要时在第二行不可用。事实上,
token
在该范围内永远不可用,因为
getJwtToken
返回一个承诺,其解析值将仅在
处理程序内可用。因此,第1步是重写此函数:

export const myStartup = (callback) => {
    getJwtToken() // returns a Promise
    .then((token) => { // token is available inside Promise's .then
        callback(token);
    })
}
现在我们看一下
getJwtToken
,记住它必须返回一个承诺,因为我们刚刚做了更改

export const getJwtToken = () => {
    let token = localStorage.getItem('myToken');
    if (token == null)
        token = getJwtTokenFromApi();
    return token;
}
这是一个有趣的案例,因为
getJwtToken
实现了分支行为,一个分支是同步的,另一个不是。(
localStorage.getItem
阻塞,但
getJwtTokenFromApi
是异步的。)处理此类情况的唯一方法是使整个函数异步:确保它始终返回承诺,即使它所需的数据可以从同步源获得

由于
localStorage.getItem
是同步的,如果我们喜欢它给我们的值,我们会在返回它之前将该值包装在承诺中。否则,我们只能返回由
getJwtTokenFromApi
返回的承诺:

export const getJwtToken = () => {
    let token = localStorage.getItem('myToken')

    if(token !== null)
        return Promise.resolve(token);

    return getJwtTokenFromApi();
}
现在,无论我们处于哪种情况,这个函数都将返回一个包含令牌的承诺

最后,我们来看看
getJwtTokenFromApi
,它做了一些事情:

  • 它构造一个
    请求
  • 它执行一个请求(异步)
  • 如果成功,它将响应转换为文本(异步)
  • 它检查文本
如果所有这些都解决了,它希望返回文本值。但是这些任务中有一半是异步的,这也意味着整个函数必须是异步的。以下是您开始使用的更精简版本:

export const getJwtTokenFromApi = () => {

    var request = new Request('/api/token', {});

    fetch(request)
    .then((response) => {
        response.text()
        .then((token) => {
            if(token.length > 0) {
                localStorage.setItem('myToken', token);
                return token;
            } else {
                return null;
            }
         })
    })
}
这里最大的问题是您没有返回
fetch
。这一点很重要,因为嵌套在其中的其他
return
语句不适用于整个函数。此函数不会返回任何写入的内容,尽管它将执行XHR调用。因此,第一个修复方法是
返回fetch<
export const getJwtTokenFromApi = () => {

    var request = new Request('/api/token', {
        method: 'GET',
        mode: 'cors',
        credentials: 'include'
    });

    return fetch(request)
    .then((response) => response.text())
    .then((token) => {
        if(token.length > 0) {
            localStorage.setItem('myToken', token);
            return token;
        }

        return null;
     })
    })
}
var x = Promise.resolve( Promise.resolve( Promise.resolve ( 10 )))
var y = Promise.resolve( 10 )
.then((value) => {
    // value === 10
})
export const getJwtTokenFromApi = () => {

    var request = new Request('/api/token', {
        method: 'GET',
        mode: 'cors',
        credentials: 'include'
    });

    return fetch(request)
    .then((response) => response.text())
    .then((token) => {
        if(token.length > 0) {
            localStorage.setItem('myToken', token);
            return token;
        }

        return null;
     })
    })
}

export const getJwtToken = () => {
    let token = localStorage.getItem('myToken')

    if(token !== null)
        return Promise.resolve(token);

    return getJwtTokenFromApi();
}

export const myStartup = (callback) => {
    getJwtToken()
    .then((token) => {
        callback(token);
    })
}
export const myStartup = () => {
    return getJwtToken();
}