kangkai124 / blog

开发笔记
https://kangkai124.github.io/blog/
MIT License
26 stars 4 forks source link

Reduce 实现 Promise 串行执行 #22

Open kangkai124 opened 5 years ago

kangkai124 commented 5 years ago
function runPromiseByQueue (myPromises) {
  myPromises.reduce(
    (previousPromise, nextPromise) => previousPromise.then(() => nextPromise()),
    Promise.resolve()
  )
}

上一个 Promise 执行,完毕后调用下一个 Promise,并作为一个新的 Promise 返回,下次迭代继续这个循环。

const createPromise = (time, id) => () =>
    new Promise(resolve => 
    setTimeout(() => {
        console.log("promise", id)
      resolve()
    }, time)
  )

runPromiseByQueue([
  createPromise(3000, 1),
  createPromise(2000, 2),
  createPromise(1000, 3),
])

// loading 3000 ms
// promise 1
// loading 2000 ms
// promise 2
// loading 1000 ms
// promise 3

Reduce 的作用是在内存中生成这个队列,而不需要把这个冗余的队列写在代码里。

kangkai124 commented 5 years ago

在 async/await 的支持下,runPromiseByQueue 函数可以更为简化:

async function runPromiseByQueue(myPromises) {
  for (let func of myPromises) {
    await func();
  }
}
kangkai124 commented 5 years ago

参考:

  1. why-using-reduce-to-sequentially-resolve-promises-works
  2. 精读《用 Reduce 实现 Promise 串行执行》
kangkai124 commented 5 years ago

更多 Promise 扩展:promise-fun

kangkai124 commented 5 years ago

Reduce Polyfill

Array.prototype.reduce2 = function(callback /*, initialValue*/) {
  if (this === null) {
    throw new TypeError('Array.prototype.reduce called on null or undefined');
  }
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }

  var thisArr = Object(this),       // thisArr 调用该方法的数组
      len = thisArr.length >>> 0,   // 数组的长度,`x >>> 0` 保证 x 为正整数
      index = 0,                    // index 下标             
      value;                        // 每次传入累加器的值

  // 如果参数长度大于等于二,说明传入了初始值,
  // 去拿下标为 1 的值作为初始值    
  if (arguments.length >= 2) {
    value = arguments[1];
  } else {
    // 判断数组里的空位,将数组里的第一个非falsy值初始化为初始值 value
    while (index < len && !(index in thisArr)) {
      index++;
    }
    // 此时传入的参数长度小于等于1,如果 len 也是0,报错,因为没有初始值
    if (index >= len) {
      throw new TypeError('Reduce of empty array with no initial value');
    }
    // 此时将数组第一项的值赋值给 value,然后下标++
    value = thisArr[index++];
  }

  // 队列开始
  while (index < len) {
    if (index in thisArr) {
      value = callback(value, thisArr[index], index, thisArr);
    }

    index++;
  }
  // 更新 value
  return value;
}
kangkai124 commented 5 years ago

var len = arr.length >>> 0 是个什么鬼

>> 是有符号右移 该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃,拷贝最左侧的位(即符号位)以填充左侧。

9 >> 2 得到 2:

9 (base 10): 00000000000000000000000000001001 (base 2)
                  --------------------------------
9 >> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)

-9 >> 2 得到 -3,因为符号被保留了。

-9 (base 10): 11111111111111111111111111110111 (base 2)
                   --------------------------------
-9 >> 2 (base 10): 11111111111111111111111111111101 (base 2) = -3 (base 10)

>>> 是无符号右移 该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃,左侧用0填充。因为符号位变成了 0,所以结果总是非负的。(即便右移 0 个比特,结果也是非负的。

对于非负数,有符号右移和无符号右移总是返回相同的结果。

但是对于负数,由于右移后左侧总是补0,所以总是得到非负数。

-9 (base 10): 11111111111111111111111111110111 (base 2)
                    --------------------------------
-9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)

知道了 >>> 是无符号右移,但是右移 0 有什么意义呢??

答案就是:

It doesn't just convert non-Numbers to Number, it converts them to Numbers that can be expressed as 32-bit unsigned ints.

所以,移位操作符做了两件事情,第一将不是 number 的数据转为 number,第二就是将 number 转为无符号的 32bit 数据, 也就是 Uint32 类型。

所以,最上面的代码的意义就是,保证 len 是数字类型,且为正整数,在无意义的情况下取值为0。