Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/qt/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
替换Javascript(ECMA 6)中的多个依赖嵌套异步调用_Javascript_Asynchronous_Callback_Ecmascript 6_Async Await - Fatal编程技术网

替换Javascript(ECMA 6)中的多个依赖嵌套异步调用

替换Javascript(ECMA 6)中的多个依赖嵌套异步调用,javascript,asynchronous,callback,ecmascript-6,async-await,Javascript,Asynchronous,Callback,Ecmascript 6,Async Await,我通过调用大量异步加载Resources的方法来初始化我的JS应用程序,每个方法都依赖于前面的方法。我直观的解决方案是将调用嵌套在回调函数(cba、cbb、cbc、cbd)中,在这些(LoadA、LoadB、LoadC、LoadD)成功完成后,分别从LoadA、LoadB、LoadC、LoadD的内部调用: app.LoadA( function cba() { app.LoadB( function cbb() { ....

我通过调用大量异步加载Resources的方法来初始化我的JS应用程序,每个方法都依赖于前面的方法。我直观的解决方案是将调用嵌套在回调函数(cba、cbb、cbc、cbd)中,在这些(LoadA、LoadB、LoadC、LoadD)成功完成后,分别从LoadA、LoadB、LoadC、LoadD的内部调用:

app.LoadA( function cba() {
        app.LoadB( function cbb() {
            ....        
            app.LoadC( function cbc() {
            ...           
                app.LoadD( function cbd() {
                    ...
                } );
            } );
        } );
    } );

LoadA( cb )
{
    let url = '.........'; // just an url
    let req = new XMLHttpRequest();
    req.open('GET', url, true);
    req.responseType = 'arraybuffer';

    let lh = this;

    req.onload = function ol(event)         
    {
        let arrayBuffer = req.response; 
        let loadedObject = Convert( arrayBuffer, ......... );
        cb( loadedObject ); // call, if successed!
    }
    req.send(null);
}
....
LoadA在不加载对象的情况下返回,因此LoadB必须等待LoadA的嵌入式onload函数调用回调cb,以此类推

我不喜欢这种嵌套解决方案,因为它很难概述和维护


我的问题是:是否有其他(专注于“快乐之路”,更好、更短、更少混乱、更容易理解和维护)的可能性来实现相同的结果?

我会为每个异步调用使用一个“承诺”来打破这一局面

MDN在这里对如何使用它们有很好的解释:

你的应用程序代码可能最终看起来像这样

app = {

    loadA: function() {
        return new Promise((resolve) => {
            console.log('Loading A');
            resolve();
        });
    },

    loadB: function () {
        return new Promise((resolve) => {
            console.log('Loading B');
            resolve();
        });
    },

    // For demonstration, I have coded loadC with a rejection if it fails
    // (Success is based on a random boolean) 
    loadC: function () {
        return new Promise((resolve, reject) => {
            console.log('Loading C');
            var success = Math.random() >= 0.5;
            if( success ){
                resolve();
            } else {
                reject('C did not load');
            }

        });
    },

    loadD: function () {
        return new Promise((resolve) => {
            console.log('Loading D');
            resolve();
        });
    }

};

// A global function for reporting errors
function logError(error) {
    // Record the error somehow
    console.log(error);
}
现在把他们叫做承诺链

app.loadA().then(app.loadB).then(app.loadC, logError).then(app.loadD);
现在,如果将来您决定更改希望调用函数的顺序,则根本不需要触摸函数中的代码,因为您使用了承诺。您只需更改包含承诺链的行:

app.loadA().then(app.loadD).then(app.loadB).then(app.loadC, logError);

(上面的示例假设您可以将应用程序上的方法更改为承诺。如果应用程序是第三方的东西,您无法编辑,则解决方案会有所不同)

我将使用每个异步调用的“承诺”来打破这一点

MDN在这里对如何使用它们有很好的解释:

你的应用程序代码可能最终看起来像这样

app = {

    loadA: function() {
        return new Promise((resolve) => {
            console.log('Loading A');
            resolve();
        });
    },

    loadB: function () {
        return new Promise((resolve) => {
            console.log('Loading B');
            resolve();
        });
    },

    // For demonstration, I have coded loadC with a rejection if it fails
    // (Success is based on a random boolean) 
    loadC: function () {
        return new Promise((resolve, reject) => {
            console.log('Loading C');
            var success = Math.random() >= 0.5;
            if( success ){
                resolve();
            } else {
                reject('C did not load');
            }

        });
    },

    loadD: function () {
        return new Promise((resolve) => {
            console.log('Loading D');
            resolve();
        });
    }

};

// A global function for reporting errors
function logError(error) {
    // Record the error somehow
    console.log(error);
}
现在把他们叫做承诺链

app.loadA().then(app.loadB).then(app.loadC, logError).then(app.loadD);
现在,如果将来您决定更改希望调用函数的顺序,则根本不需要触摸函数中的代码,因为您使用了承诺。您只需更改包含承诺链的行:

app.loadA().then(app.loadD).then(app.loadB).then(app.loadC, logError);
(以上示例假设您可以更改应用程序上的方法,使其作为承诺工作。如果应用程序是第三方,您无法编辑,则解决方案将有所不同)

下面是“回调地狱”与使用
async/await
可以实现的更好代码的比较:

//虚拟实现:
//这些函数在100ms后调用值为1到4的cb
const loadA=(cb)=>setTimeout(=>cb(1),100);
const loadB=(cb)=>setTimeout(=>cb(2),100);
const loadC=(cb)=>setTimeout(=>cb(3),100);
const loadD=(cb)=>setTimeout(=>cb(4),100);
函数callbackHell(){
loadA(功能cba(a){
loadB(功能cbb(b){
loadC(功能cbc(c){
loadD(功能cbd(d){
日志([a,b,c,d]);
});
});
});
});
}
异步函数nicerCode(){
常数res=[
等待新的承诺(loadA),
等待新的承诺(loadB),
等待新的承诺(loadC),
等待新的承诺(loadD)
];
控制台日志(res);
}
callbackHell();
nicerCode()
.as控制台包装{max height:100%!important;top:0;}
这里比较了“回调地狱”和使用
async/await可以实现的更好的代码:

//虚拟实现:
//这些函数在100ms后调用值为1到4的cb
const loadA=(cb)=>setTimeout(=>cb(1),100);
const loadB=(cb)=>setTimeout(=>cb(2),100);
const loadC=(cb)=>setTimeout(=>cb(3),100);
const loadD=(cb)=>setTimeout(=>cb(4),100);
函数callbackHell(){
loadA(功能cba(a){
loadB(功能cbb(b){
loadC(功能cbc(c){
loadD(功能cbd(d){
日志([a,b,c,d]);
});
});
});
});
}
异步函数nicerCode(){
常数res=[
等待新的承诺(loadA),
等待新的承诺(loadB),
等待新的承诺(loadC),
等待新的承诺(loadD)
];
控制台日志(res);
}
callbackHell();
nicerCode()

.as控制台包装{max height:100%!important;top:0;}
要避免回调地狱,您需要使用

如果
loadA,…,loadN
函数返回承诺,那么您只需依次在每个函数之后调用
.then()

loadA().then(function() {
  loadB().then(function() {
    loadC().then(...
  });
});
现在重要的是要记住,
.then()
返回一个与参数值解析的承诺

因此,如果
loadA
loadB
都返回承诺,您可以像这样链接它们:

loadA().then(function() {
  return loadB();
).then(...)
这相当于:

loadA().then(loadB).then(loadC).then(...)
简单多了

如果你的函数没有返回一个承诺,那么你需要用一个helper函数将它们封装在一个承诺中

function wrapInsidePromise(f) {
  return new Promise(function(resolve, reject) {
    f(function() {
      resolve();
    });
  });
}

var pLoadA = wrapInsidePromise(app.loadA);
var pLoadB = wrapInsidePromise(app.loadB);
...

pLoadA().then(pLoadB).then(...);
更重要的是,在ES6中,您可以使用
async/await
,这使您能够以异步方式使用承诺


要避免回调地狱,您需要使用

如果
loadA,…,loadN
函数返回承诺,那么您只需依次在每个函数之后调用
.then()

loadA().then(function() {
  loadB().then(function() {
    loadC().then(...
  });
});
现在重要的是要记住,
.then()
返回一个与参数值解析的承诺

因此,如果
loadA
loadB
都返回承诺,您可以像这样链接它们:

loadA().then(function() {
  return loadB();
).then(...)
这相当于:

loadA().then(loadB).then(loadC).then(...)
简单多了

如果你的函数没有返回一个承诺,那么你需要用一个helper函数将它们封装在一个承诺中

function wrapInsidePromise(f) {
  return new Promise(function(resolve, reject) {
    f(function() {
      resolve();
    });
  });
}

var pLoadA = wrapInsidePromise(app.loadA);
var pLoadB = wrapInsidePromise(app.loadB);
...

pLoadA().then(pLoadB).then(...);
更重要的是,在ES6中,您可以使用
async/await
,这使您能够以异步方式使用承诺


我保证有更好的方法:@Trantor上面的代码将类似于
app.LoadA.then(app.LoadB)。then(app.LoadC)。then(app.LoadD)。then(…)
。这真的更让人困惑吗?@Andy那么你最好等待OP的回复。(对不起,这花了我太长时间)@evolutionxbox谢谢你的延迟回复。@Andy没问题。我希望这有助于解决这个问题