JS 承诺里面的承诺

Posted

技术标签:

【中文标题】JS 承诺里面的承诺【英文标题】:JS promise inside promise 【发布时间】:2017-05-18 12:31:44 【问题描述】:

应该在 3 次尝试中检索位置的 getLocation() 函数改为返回 undefinednavigator.geolocation.getCurrentPosition() 返回正确的位置,但问题出在 Promise 处理中。

问题显然是我在 Promise 中调用了 Promise。我不允许在 geolocate() 中使用已声明为 asyncawait 关键字。

原来的电话:

var getLocationPromise = this.getLocation();
// Do something...
location = await getLocationPromise;

getLocation():

  async getLocation() 
    return new Promise((resolve, reject) => 
      var geolocate;
      for (let i=0; i<3; i++) 

        geolocate = this.geolocate();

        try 
            var location = geolocate;//CAN'T USE AWAIT INSIDE ASYNC...
            resolve(location);
         catch(err) 
            continue;
        
       
      reject("Max geolocation attempts");
    );
  

geolocate():

  async geolocate() 
    return new Promise((resolve, reject) => 

      navigator.geolocation.getCurrentPosition(
        (position) => 
          resolve(position);
        ,
        (err) => 
          reject(err);
        ,
        enableHighAccuracy: true, timeout: 20000, maximumAge: 1000
      );
    );
  

【问题讨论】:

await getLocation().then(); === await getLocation(); - 不是吗? 它给出了不同的结果。我对await getLocation(); 返回undefined 有问题,所以我使用await getLocation().then();,这似乎更安全 不知道为什么你在geolocategetLocation 上使用async - 因为这些函数都没有使用await //CAN'T USE AWAIT INSIDE ASYNC - 不,这是不对的,你只能在async中使用await 这就是问题所在,不能await in async 【参考方案1】:

只要以下是在一个声明为异步的函数中

var getLocationPromise = this.getLocation();
// Do something...
location = await getLocationPromise;

应该没问题

看getLocation/geolocate,除非你需要单独的geolocate方法,它们应该可以合并简化为

getLocation() 
    var geolocate = () =>
        new Promise((resolve, reject) => 
            navigator.geolocation.getCurrentPosition(resolve, reject, 
                enableHighAccuracy: true,
                timeout: 20000,
                maximumAge: 1000
            );
        );
    // this function will "retry" the supplied function (fn) cont times
    var limitedPromiseRetry = (fn, cont) => fn().catch(err => cont > 0 ? limitedPromiseRetry(fn, cont-1) : Promise.reject('Max number of geolocation attempts'));
    return limitedPromiseRetry(geolocate, 3);

【讨论】:

不错且紧凑的解决方案,它避免了使用双重承诺。从您的回复中,我了解到将 promise 放入 promise 不是一个好主意并且不受支持? 不,支持,只是在这种情况下不需要【参考方案2】:

异步和等待

你不能在函数中使用await 没有 async 关键字。所以报错是因为executor函数是notasync:

var getLocation = async function() // <-- this function has "async" keyword, but ...
    return new Promise( function( resolve, reject ) // <-- ... this "executor" function has no "async" keyword.
        var value = await geolocate();               // <-- ERROR: await is only valid in async function.
        resolve( value );
    )
;

但您不应该将 Promise executor 设为 async 函数。 请参阅https://eslint.org/docs/rules/no-async-promise-executor 了解更多信息。

new Promise( async function( resolve, reject ) // <-- BAD ! Don't do it !
   ...
)

没有嵌套的承诺

但由于getLocation 已经是一个承诺,你根本不需要嵌套的new Promise( ... )

var getLocation = async function() // <-- "async" makes a function to be a promise
    var value = await geolocate();

    // "return value" inside async function is "the same" as
    // "resolve( value )" in a promise
    return value;
;

因此,理论上,您可以通过以下方式解决您的问题(尽管可能有更好的方法。对于异步函数内部的“拒绝”,另请参阅How to reject in async/await syntax?)。

var getLocation = async function()
    for( let i = 0; i < 3; i++ )
        try 
            console.log('try ...', i);
            var location = await geolocate(i);
            console.log('... success');
            return location;
         catch(err) 
            console.log('... next try');
            continue;
        
    
    return Promise.reject('no success');
;

getLocation().then(function(result)
    console.log('then:', result);
).catch(function(reason)
    console.log('error:', reason);
)

嵌套承诺

Promise 中的 Promise 是可以的。 请注意,使用已解决的 Promise 解决 Promise 的行为“相同”,就像只解决一个 Promise。你不会注意到 .then() 函数有任何区别,无论你是解析一个值,还是在一个 Promise 中解析一个 Promise,......等等。

var nestedPromises = new Promise( function( resolve1, reject1 )
    resolve1( new Promise( function( resolve2, reject2 )
        resolve2( new Promise( function( resolve3, reject3 )
            resolve3('resolved value');
        ));
    ));
);

nestedPromises.then( function( value )
    console.log('value:', value);   // <-- "value: resolved value"
)

【讨论】:

以上是关于JS 承诺里面的承诺的主要内容,如果未能解决你的问题,请参考以下文章

在 Express.js 中停止执行 Sequelize 承诺

挣扎于js承诺,foreach承诺添加结果。

JS/jQuery 承诺

外部函数调用,承诺,异步和Mongo - 混淆

返回嵌套承诺但未找到响应

Node.js 从函数返回一个承诺