从"concurency"异步函数中"Lock"块



即使javascript在单线程中运行,异步函数中仍可能出现并发问题。其中一些可以通过大大增加代码的复杂性来避免,但有些我像这样解决:

// private "lock"
let _lock = null;
// this function waits till the last call is done, then 
// initiates next one
async function doTheStuff() {
    while (_lock) {
        await _lock;
    }
    _lock = actuallyDoTheStuff();
    const result = await _lock;
    _lock = null;
    return result;
}
async function actuallyDoTheStuff() {
    // this function really does the stuff
}

这可确保只有一个actuallyDoTheStuff实例正在运行,但它看起来并不那么好。

这真的行得通吗?我能确定不会有无休止的循环/锁定吗?

而且,无论它是否有效,难道没有更好的方法来做到这一点吗?

我会将所有内容封装在 actuallyDoTheStuff 中,它只是在它生成的最后一个 Promise 上调用 .then

const actuallyDoTheStuff = (() => {
  let lastProm = Promise.resolve();
  return () => {
    const nextProm = lastProm.then(() => {
      return new Promise(resolve => setTimeout(() => {
        console.log('resolving');
        resolve();
      }, 1000));
    });
    lastProm = nextProm;
    return lastProm;
  };
})();
console.log('start');
actuallyDoTheStuff();
actuallyDoTheStuff();
actuallyDoTheStuff();
setTimeout(() => {
  actuallyDoTheStuff();
  actuallyDoTheStuff();
}, 200);

如果可能会抛出,则在重新分配给lastProm时添加一个catch

const actuallyDoTheStuff = (() => {
  let lastProm = Promise.resolve();
  return () => {
    const nextProm = lastProm.then(() => {
      return new Promise(resolve => setTimeout(() => {
        console.log('resolving');
        resolve();
      }, 1000));
    });
    lastProm = nextProm.catch(() => null);
    return nextProm;
  };
})();
console.log('start');
actuallyDoTheStuff();
actuallyDoTheStuff();
actuallyDoTheStuff();
setTimeout(() => {
  actuallyDoTheStuff();
  actuallyDoTheStuff();
}, 200);

我不确定actuallyDoTheStuff最终应该做什么,但是如果您尝试对它的多个调用进行排序(并等待每次调用(,您可以doTheStuff一个异步包装器函数,其中包含在每次迭代时等待actuallyDoTheStuff for loop

function actuallyDoTheStuff( iteration ) {
  console.log( "Waiting...")
  return new Promise( res => {
    setTimeout( () => {
      res( iteration );
    }, 150 );
  } );
}
async function doTheStuff() {
  for ( let i = 0; i <= 5; i++ ) {
    const result = await actuallyDoTheStuff( i );
    console.log( result );
  }
}
doTheStuff();

或者actuallyDoTheStuff做一个递归函数:

let index = 1;
async function actuallyDoTheStuff( i ) {
  if ( i <= 5 ) {
    console.log( "Waiting..." )
    await new Promise( res => {
      setTimeout( () => {
        console.log( i );
        i++
        res();
        actuallyDoTheStuff( i );
      }, 150 );
    } );
  }
}
actuallyDoTheStuff( index );

最新更新