js如何锁住代码执行?

bubbfish
  • 73
北京

有三个语句
let value = A();
let value = B();
let value = C();

while(true){
let value = A();
let value = B();
let value = C();
}
ABC执行也可能是无序的,它门是重复执行的,我现在如何保证执行的语句要在它之前的语句都拿到结果后在执行?
可能猜测需要个队列,需要个锁之类的;
但是不知道怎么实现,有大佬嘛 集思广益下;

目前我搜到promise队列,但是队列是有限的,我这个无限的怎么弄,需要添加任务到队列之类的。

回复
阅读 1.2k
4 个回答
Mannix
  • 2.1k

push() 添加到队列,clear() 清空队列并返回全部结果

let head, tail

function push(promise) {
  if (!head) {
    head = tail = { promise }
  } else {
    tail = tail.next = { promise }
  }
  return promise
}

async function clear() {
  const all = []
  while (head) {
    all.push(await head.promise)
    head = head.next
  }
  tail = undefined
  return all
}

const A = () => new Promise(resolve => setTimeout(() => resolve('A'), Math.random() * 1000))
const B = () => new Promise(resolve => setTimeout(() => resolve('B'), Math.random() * 1000))
const C = () => new Promise(resolve => setTimeout(() => resolve('C'), Math.random() * 1000))

async function run(tot) {
  while (tot--) {
    if (Math.random() < 0.2) console.log(await clear())
    push(A()).then(r => console.log(r))
    push(B()).then(r => console.log(r))
    push(C()).then(r => console.log(r))
  }
}

run(10)

// 模拟在 clear() 期间追加任务

const D = () => new Promise(resolve => setTimeout(() => resolve('D'), 1000))

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

async function runD(tot) {
  while (tot--) {
    await sleep(Math.random() * 2000)
    push(D()).then(r => console.log(r))
  }
}

runD(3)
B
C
A
C
C
A
B
B
A
D
[
  'A', 'B', 'C', 'A',
  'B', 'C', 'A', 'B',
  'C', 'D'
]
A
B
C
[ 'A', 'B', 'C' ]
C
B
A
D
D
[ 'A', 'B', 'C', 'D', 'D' ]
C
A
B
A
A
B
C
B
C
C
A
B
B
C
A

如果没有在 clear() 期间追加任务的场景,可直接用 Promise.all() 等方法

const promises = []

const A = () => new Promise(resolve => setTimeout(() => resolve('A'), Math.random() * 1000))
const B = () => new Promise(resolve => setTimeout(() => resolve('B'), Math.random() * 1000))
const C = () => new Promise(resolve => setTimeout(() => resolve('C'), Math.random() * 1000))

async function run(tot) {
  while (tot--) {
    if (Math.random() < 0.2) {
      console.log(await Promise.all(promises))
      promises.length = 0
    }
    promises.push(A())
    promises.push(B())
    promises.push(C())
  }
}

run(20)

你说的ABC执行是无序的,我可以理解为里面包含异步代码么。
如果是这样的,就得给方法封装好 Promise,然后使用 async/await 使它同步运行

async function funcAll () {
    while(true){
      let value1 = await A();
      let value2 = await B();
      let value3 = await C();
    }
}

看你这个意思是想执行完队列之后再执行某些操作?考虑一下 Promise.all 或者 Promise.allSettled ?

如果这个队列会动态改变的,也就是说队列执行过程中会追加执行任务的,那么就需要你自己写一个队列函数了。

爱热闹的路灯
  • 17

不知道理解对不对,假设A、B、C三个函数的定义如下:

const sleepAndOutput = (msg, sleepTime = Math.round(500 + Math.random() * 1500)) => {
    return new Promise((resolve) => {
        setTimeout(()=>{
            console.log(`${msg}: ${sleepTime}ms`);
            resolve(`${msg} - ${sleepTime}ms`);
        }, sleepTime);
    })
};

const A = sleepAndOutput.bind(undefined, "A", 0);
const B = sleepAndOutput.bind(undefined, "B", 800);
const C = sleepAndOutput.bind(undefined, "C", 1500);

那么把函数前处理一下,让他们每一次执行都会操作一个num值自增,执行完毕后num自减,当num的值减为0即表示运行完毕了。

const preRunAll = (oldA, oldB, oldC, callback) => {
    return new Promise((resolve) => {
        let num = 0;
        let readyFlag = false;
        const startMission = async (func) => {
            try {
                num++;
                return await func();
            } finally {
                num--;
                if (0 >= num && readyFlag) {
                    setTimeout(resolve, 0);
                }
            }
        }
        const newA = startMission.bind(undefined, oldA);
        const newB = startMission.bind(undefined, oldB);
        const newC = startMission.bind(undefined, oldC);
        callback(newA, newB, newC);

        readyFlag = true;
        startMission(async () => {return undefined});
    });
}

处理完后的函数会变成回调返回,这里的ABC函数不再是原函数,只是名称一样

const printResult = (msg)=>{
    console.log("printResult:", msg)
};
// 回调可使用async也可不用
preRunAll(A, B, C, async (A, B, C)=>{
    // 前运行的ABC代码,注意此处的ABC函数为处理后的函数,并非原函数,可以使用.then收集数据
    A().then(printResult);
    A().then(printResult);
    A().then(printResult);
    B().then(printResult);
    A().then(printResult);
    C().then(printResult);
}).then(() => {
    console.log("finish:");
    // 后运行ABC代码,ABC均为原函数
    B().then(printResult);
    C().then(printResult);
    A().then(printResult);
});
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏