异步控制流程 遍历篇each

异步控制流程的目的:

  • 对异步操作提供类似同步遍历的操作
  • 本文的Promise兼容均为在原本只支持node回调的方式下修改

基础方法

onlyOnce 只执行一次,第二次报错

  • 只执行一次,因为第二次func变成了抛出错误
function onlyOnce(func) {
  return function(err, res) {
    var fn = func;
    func = throwError;
    fn(err, res);
  };
}

once 只执行一次,第二次无效

  • 只执行一次,因为第二次func变成了空函数
 function once(func) {
   return function(err, res) {
     var fn = func;
     func = noop;
     fn(err, res);
   };
 }

iteratorSymbol 判断是否具有迭代器并返回迭代器

var iteratorSymbol = typeof Symbol === func && Symbol.iterator;

arrayEach 普通数组遍历


 - iterator:每次遍历的回调
 - callback:内部有个计数器,执行遍历完成后的回调

function arrayEach(array, iterator, callback) {
  var index = -1;
  var size = array.length;
  // 三个参数情况
  if (iterator.length === 3) {
    while (++index < size) {
      // 传入value、key、手动计数最后遍历完成回调(只有第一次执行有效)
      iterator(array[index], index, onlyOnce(callback));
    }
  } else { // 两个参数情况
    while (++index < size) {
      iterator(array[index], onlyOnce(callback));
    }
  }
}

baseEach 对象类型遍历

function baseEach(object, iterator, callback, keys) {
  var key;
  var index = -1;
  var size = keys.length;
  // 三个参数情况
  if (iterator.length === 3) {
    while (++index < size) {
      key = keys[index];
      iterator(object[key], key, onlyOnce(callback));
    }
  } else { // 两个参数情况
    while (++index < size) {
      iterator(object[keys[index]], onlyOnce(callback));
    }
  }
}

symbolEach 具有迭代器类型遍历

  function symbolEach(collection, iterator, callback) {
    var iter = collection[iteratorSymbol]();
    var index = 0;
    var item;
	// 三个参数情况
    if (iterator.length === 3) {
      while ((item = iter.next()).done === false) {
        iterator(item.value, index++, onlyOnce(callback));
      }
    } else {// 两个参数情况
      while ((item = iter.next()).done === false) {
        index++;
        iterator(item.value, onlyOnce(callback));
      }
    }
    return index;
  }

timeSync 一次执行多少个迭代器

  • 限制执行次数
function timesSync(n, iterator) {
  var index = -1;
  while (++index < n) {
    iterator(index);
  }
}

异步遍历

each

  • 实现 forEach 效果
  • 原理:每次回调中执行计数器,判断计数完毕时,执行结束回调
exports.each=createEach(arrayEach, baseEach, symbolEach)

  function createEach(arrayEach, baseEach, symbolEach) {
    return function each(collection, iterator, callback) {
      let promise=new Promise((resolve,reject)=>{
        callback = once(callback || noop);
        var size, keys;
        var completed = 0;
        if (isArray(collection)) {
          size = collection.length;
          arrayEach(collection, iterator, done);
        } else if (iteratorSymbol && collection[iteratorSymbol]) {
          size = symbolEach(collection, iterator, done);
          size && size === completed && callback(null);
        } else if (typeof collection === obj) {
          // Object.keys
          keys = nativeKeys(collection);
          size = keys.length;
          baseEach(collection, iterator, done, keys);
        }
        if (!size) {
          callback(null);
          reject('value should be an object');
        }
  
        function done(err, bool) {
          if (err) {
            callback = once(callback);
            callback(err);
            reject(err);
          } else if (++completed === size) {
            callback(null);
            resolve('null')
          } else if (bool === false) {
            callback = once(callback);
            callback(null);
            reject('cancel');
          }
        }
      })
      return promise.then((res)=>res).catch(err=> Promise.reject(err))
    };
  }

基本使用:

const array = { a: 1, b: 3, c: 2 };;
const iterator = function(key, value,done) {
  setTimeout(function() {
    done()
    // done() done只有第一次执行有效
    // done(err)、done(null,false) 结束遍历并执行最终回调
  }, key * 1000);
};

// 回调方式
nac.each(array, iterator, function(err) {
    console.log('done')
});

// Promise方式
async function neo(){
    try {
        let res=await nac.each(array, iterator);
        console.log('success',res);    
    } catch (error) {
        console.log('error',error)
    }
        
    
}

eachLimit

  • 不保证回调顺序,限制每一次异步请求的次数
  • 原理:只需要限制第一次发起请求的次数,然后在每一次请求回调中调用后续请求,即可实现每一次请求数量小于n

基本使用

var array = [1, 5, 3, 4, 2];
var iterator = function(value,key,done) {
  setTimeout(function() {
    done(null, num % 2);
  }, num * 10);
};
async.everyLimit(array, 2, iterator, function(err, res) {
  console.log(res); // false
  console.log(order); // [1, 3, 5, 2]
});
function eachLimit(collection, limit, iterator, callback) {
  callback = callback || noop;
  var size, index, key, keys, iter, item, iterate;
  var sync = false;
  var started = 0;
  var completed = 0;

  if (isArray(collection)) {
    size = collection.length;
    iterate = iterator.length === 3 ? arrayIteratorWithIndex : arrayIterator;
  } else if (!collection) {
  } else if (iteratorSymbol && collection[iteratorSymbol]) {
    size = Infinity; // 注意迭代器类型不知道size有多少个
    iter = collection[iteratorSymbol]();
    iterate = iterator.length === 3 ? symbolIteratorWithKey : symbolIterator;
  } else if (typeof collection === obj) {
    keys = nativeKeys(collection);
    size = keys.length;
    iterate = iterator.length === 3 ? objectIteratorWithKey : objectIterator;
  } else {
    return callback(null);
  }
  if (!size || isNaN(limit) || limit < 1) {
    return callback(null);
  }
  timesSync(limit > size ? size : limit, iterate);
  // 数组的遍历器,根据索引进行遍历
  function arrayIterator() {
    if (started < size) {
      iterator(collection[started++], done);
    }
  }

  function arrayIteratorWithIndex() {
    index = started++;
    if (index < size) {
      iterator(collection[index], index, done);
    }
  }
  // symbol类型的遍历
  function symbolIterator() {
    item = iter.next();
    if (item.done === false) {
      started++;
      iterator(item.value, done);
    } else if (completed === started && iterator !== noop) { // completed === started 表明已迭代完毕,createCallback对于迭代器类型只负责++completed
      iterator = noop;
      callback(null);
    }
  }

  function symbolIteratorWithKey() {
    item = iter.next();
    if (item.done === false) {
      iterator(item.value, started++, done);
    } else if (completed === started && iterator !== noop) {
      iterator = noop;
      callback(null);
    }
  }
  // 对象的遍历
  function objectIterator() {
    if (started < size) {
      iterator(collection[keys[started++]], done);
    }
  }

  function objectIteratorWithKey() {
    index = started++;
    if (index < size) {
      key = keys[index];
      iterator(collection[key], key, done);
    }
  }

  function done(err, bool) {
    if (err || bool === false) {
      iterate = noop;
      callback = once(callback);
      callback(err);
    } else if (++completed === size) {
      iterator = noop;
      iterate = throwError;
      callback = onlyOnce(callback);
      callback(null);
    } else if (sync) {
      // procces.nextTick、setImmediate、setTimeout
      nextTick(iterate);
    } else {
      sync = true;
      iterate();
    }
    sync = false;
  }
}

eachSeries

  • 保证异步串行
  • 原理:在每一次异步回调后再调用后续异步方法

基本使用:

var order = [];
var array = [1, 3, 2];
var iterator = function(value, done) {
  setTimeout(function() {
    order.push(num);
    done();
  }, num * 10);
};
async.eachSeries(array, iterator, function(err, res) {
  console.log(res); // undefined
  console.log(order); // [1, 3, 2]
});
function eachSeries(collection, iterator, callback) {
  callback = onlyOnce(callback || noop);
  var size, key, keys, iter, item, iterate;
  var sync = false;
  var completed = 0;

  if (isArray(collection)) {
    size = collection.length;
    iterate = iterator.length === 3 ? arrayIteratorWithIndex : arrayIterator;
  } else if (!collection) {
  } else if (iteratorSymbol && collection[iteratorSymbol]) {
    size = Infinity;
    iter = collection[iteratorSymbol]();
    iterate = iterator.length === 3 ? symbolIteratorWithKey : symbolIterator;
  } else if (typeof collection === obj) {
    keys = nativeKeys(collection);
    size = keys.length;
    iterate = iterator.length === 3 ? objectIteratorWithKey : objectIterator;
  }
  if (!size) {
    return callback(null);
  }
  iterate();

  function arrayIterator() {
    iterator(collection[completed], done);
  }

  function arrayIteratorWithIndex() {
    iterator(collection[completed], completed, done);
  }

  function symbolIterator() {
    item = iter.next();
    item.done ? callback(null) : iterator(item.value, done);
  }

  function symbolIteratorWithKey() {
    item = iter.next();
    item.done ? callback(null) : iterator(item.value, completed, done);
  }

  function objectIterator() {
    iterator(collection[keys[completed]], done);
  }

  function objectIteratorWithKey() {
    key = keys[completed];
    iterator(collection[key], key, done);
  }
  // 在异步回调中调用后续方法
  function done(err, bool) {
    if (err) {
      callback(err);
    } else if (++completed === size || bool === false) {
      iterate = throwError;
      callback(null);
    } else if (sync) {
      nextTick(iterate);
    } else {
      sync = true;
      iterate();
    }
    sync = false;
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值