在返回promise的函数中调用递归回调

时间:2018-07-11 11:57:21

标签: javascript html asynchronous promise

我有一个将节点附加到dom的函数,如果该节点是具有“ src”属性的脚本标签,它将为“ load”事件设置一个事件监听器,并且回调是相同的函数,因此当js标签将完成执行相同的功能,将再次被调用,并继续将节点精确地附加到其剩余的位置(使用文档片段) 伪代码示例:

'timeout'

现在,我有另一个函数,即执行该函数的“主要”函数。 function appender(){// i'm calling the function with bind so 'this' is my object while(documentfragment.children.length > 0){ if(scriptwithsrc){ node.addeventlistener("load",appender.bind(this)); parentnode.appendchild(node); return; } //if its not a script tag than just append the node parentnode.appendchild(node); return Promise.resolve(); 函数完成后,我使用“然后”继续执行。 伪代码示例:

appender

问题是,如果片段包含带有src的脚本标签,该函数将仅返回;而不是兑现承诺,所以我得到

let appenderBinder = appender.bind(Myobject); Myobject.fragment = fragment1; appenderBinder().then(() => { //first execution for fragment1 is over lets continue to fragment2 //we call it again but change our object before that Myobject.fragment = fragment2; appenderBinder().then(() => { //some more code })}) ”,

但是我需要使用return来退出该函数,并等待回调再次执行它,并继续从同一位置追加,并且只有在完成追加每个节点之后,promise才应返回,然后返回“ .then ()”应执行。 有什么想法吗?

2 个答案:

答案 0 :(得分:2)

取自mozzila的MDN:

  

使用new关键字及其构造函数创建Promise对象。该构造函数将一个称为“执行程序函数”的函数作为其参数。此函数应采用两个函数作为参数。当异步任务成功完成并返回任务结果作为值时,将调用这些函数中的第一个(解析)。当任务失败时,将调用第二个(拒绝),并返回失败原因,通常是一个错误对象。

伴随以下代码示例:

const myFirstPromise = new Promise((resolve, reject) => {
// do something asynchronous which eventually calls either:
//
//   resolve(someValue); // fulfilled
// or
//   reject("failure reason"); // rejected
});

那么这如何适用于您的问题:

如果遇到脚本标记,则要中断一个promise链,在这种情况下,您要先等待其加载,然后再继续向下链。为此,您需要稍微更改代码。

if(scriptwithsrc){
  node.addeventlistener("load",appender.bind(this));
  parentnode.appendchild(node);
  return; //This line here is the problem
}

以上代码摘自您的文章,并且正在同步运行,而您正在做的是,您没有返回任何值。由于不返回承诺,因此无法在随后可用的承诺链中使用您的函数。相反,您需要做的是:

if(scriptwithsrc){
  let loadingDone = new Promise(function(res, rej){
    node.addEventListener("load", res)
    parentnode.appendchild(node)
  }
  return loadingDone; // This line returns the promise as usual
}
else{
  parentnode.appendchild(node)
  return Promise.resolve() //And so does this line
}

一旦脚本标签加载,它将触发res函数,该函数将解决诺言,您的链将继续。在这种特殊情况下,mozzila代码示例的异步事物部分涉及脚本标记的加载。

编辑:由于从最初的帖子来看不是很清楚,后来在注释中得到了澄清,因此该链需要从它在当前片段中停止的地方继续,因此根据请求,添加了一个工作示例来反映这一点。

工作示例:

function appender(arg) {
    let i;
    for (i = 0; i < arg.length; i++) {
        if (arg[i].script) {
            let chainedPromise;
            let pauseProm = new Promise(function (res, rej) {
                setTimeout(function () {
                    console.log('Appended element: ', arg[i].name);
                    res();
                }, 2000); //to mimic script load;
            });
            let j;
            let newChain = [];
            for (j = i + 1; j < arg.length; j++) {
                newChain.push(arg[j]);
            }
            chainedPromise = pauseProm.then(function () {
                return appender(newChain)
            });
            return chainedPromise;
        } else
            console.log('Appended element: ', arg[i].name);
    }
    return Promise.resolve();
}

function Main() {
    appender([
        {name: '1', script: false},
        {name: '2', script: false},
        {name: '3', script: true}
    ]).then(function () {
        appender([
            {name: '4', script: false},
            {name: '5', script: false},
            {name: '6', script: true},
            {name: '7', script: true}
        ]).then(function () {
            appender([
                {name: '8', script: false},
                {name: '9', script: true},
                {name: '10', script: false},
                {name: '11', script: true},
                {name: '12', script: true}
            ]).then(function () {
                console.log('done')
            });
        });
    });
}
Main();

答案 1 :(得分:1)

  

仅在完成每个节点的追加之后,诺言应返回

否,您将需要立即创建并返回承诺。完成附加所有节点后,应实现承诺。所以你会写

function appender() {
  if (documentfragment.children.length > 0){
    if (scriptwithsrc) {
      const promise = new Promise(resolve => {
//                    ^^^^^^^^^^^
        node.addeventlistener("load", resolve);
//                                    ^^^^^^^
        parentnode.appendchild(node);
      });
      return promise.then(appender.bind(this));
//           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    } else { // its not a script tag then just append the node
      parentnode.appendchild(node);
      return appender.call(this); // recurse
    }
  } else {
    return Promise.resolve();
  }
}