js异步

1
const fs = require('fs')
console.log('start reading a file...')
fs.readFile('file.md', 'utf-8', function (err, content) {  
  if (err) {
    console.log('error happened during reading the file')
    return console.log(err)
  }

  console.log(content)
})

将一个函数作为另一个函数的参数,在这个函数的内部可以调用被传递进来的函数(即回调函数)。 这也正是回调函数诞生的原因:如果你将一个函数作为参数传递给另一个函数(此时它被称为高阶函数),那么在函数内部, 你可以调用这个函数来来完成相应的任务。回调函数没有返回值(不要试图用return),仅仅被用来在函数内部执行某些动作。

async

1
async.map([1, 2, 3], AsyncSquaringLibrary.square,  
	function(err, result){
		// result will be [1, 4, 9]
});

Promise

1
Something.save()  
	.then(function() {
		console.log('success');
	})
	.catch(function() {
		//error handling
	})

Generators/ yield

在执行一个函数的时候,你可以在某个点暂停函数的执行,并且做一些其他工作,然后再返回这个函数继续执行, 甚至是携带一些新的值,然后继续执行。

当我们调用一个生成器函数的时候,它并不会立即执行, 而是需要我们手动的去执行迭代操作(next方法)。也就是说,你调用生成器函数,它会返回给你一个迭代器。迭代器 会遍历每个中断点。

1
function* foo () {  
	var index = 0;
	while (index < 2) {
		yield index++; //暂停函数执行,并执行yield后的操作
	}
}
var bar =  foo(); // 返回的其实是一个迭代器

console.log(bar.next());    // { value: 0, done: false }  
console.log(bar.next());    // { value: 1, done: false }  
console.log(bar.next());    // { value: undefined, done: true }

co

1
co(function* (){  
	yield Something.save();
}).then(function() {
	// success
})
  .catch(function(err) {
	//error handling
});

Async/ await

1
async function save(Something) {  
	try {
		await Something.save(); // 等待await后面的代码执行完,类似于yield
	} catch (ex) {
		//error handling
	}
	console.log('success');
}

promise
它表示如A调用一个长时间任务B的时候,B将返回一个”承诺“给A,A不用关心整个实施的过程,继续做自己的任务;当B实施完成的时候,会通过A,并将执行A之间的预先约定的回调。而deferred在英语中语义为:”延迟“,这也说明promise解决的问题是一种带有延迟的事件,这个事件会被延迟到未来某个合适点再执行。

Promise/A+规范:

  • Promise 对象有三种状态: Pending – Promise对象的初始状态,等到任务的完成或者被拒绝;Fulfilled – 任务执行完成并且成功的状态;Rejected – 任务执行完成并且失败的状态;
  • Promise的状态只可能从“Pending”状态转到“Fulfilled”状态或者“Rejected”状态,而且不能逆向转换,同时“Fulfilled”状态和“Rejected”状态也不能相互转换;
  • Promise对象必须实现then方法,then是promise规范的核心,而且then方法也必须返回一个Promise对象,同一个Promise对象可以注册多个then方法,并且回调的执行顺序跟它们的注册顺序一致;
  • then方法接受两个回调函数,它们分别为:成功时的回调和失败时的回调;并且它们分别在:Promise由“Pending”状态转换到“Fulfilled”状态时被调用和在Promise由“Pending”状态转换到“Rejected”状态时被调用。
1
$http.get('/demo1') .then(function(data){
  console.log('demo1', data);
  return $http.get('/demo2', {params: data.result}); })
          .then(function(data){
          console.log('demo2', data);
                return $http.get('/demo3', {params: data.result}); })
                        .then(function(data){
  console.log('demo3', data.result);
});

$q.all([$http.get('/demo1'), $http.get('/demo2'), $http.get('/demo3') ])
.then(function(results){
    console.log('result 1', results[0]);
    console.log('result 2', results[1]);
    console.log('result 3', results[2]);
});

在Angular中的路由机制ngRoute、uiRoute的resolve机制也是采用同样的原理:在路由执行的时候,会将获取模板的Promise、获取所有resolve数据的Promise都拼接在一起,同时并行的获取它们,然后等待它们都结束的时候,才开始初始化ng-view、ui-view指令的scope对象,以及compile模板节点,并插入页面DOM中,完成一次路由的跳转并且切换了View,将静态的HTML模板变为动态的网页展示出来。

使用Promise的方式包装,使得项目Service的返回接口统一。

1
var data = $window.localStorage.getItem('data-api-key'); return $q.when(data);

对于延迟任务的Promise DSL语义化封装
Promise是延迟到未来执行某些特定任务,在调用时候则给消费者返回一个”承诺“,消费者线程并不会被阻塞。在消费者接受到”承诺“之后,消费者就不用再关心这些任务是如何完成的,以及督促生产者的任务执行状态等。直到任务完成后,消费者手中的这个”承诺“就被兑现了。

1
$modal.open({ templateUrl: '/templates/modal.html', controller: 'ModalController', controllerAs: 'modal', resolve: { } })
.result
.then(function ok(data) { // 用户点击ok按钮事件 }, function cancel(){ // 用户点击cancel按钮事件 });

这是因为modal在open方法的返回值中给了我们一个Promise的result对象(承诺)。等到用户在模态窗口中点击了ok按钮,则Bootstrap会使用$q的defer来resolve来执行ok事件;相反,如果用户点击了cancel按钮,则会使用$q的defer来reject执行cancel事件。

1
function open(data){
  var defer = $q.defer();
  // resolve or reject defer;
  var promise = defer.promise;
  promise.ok = function(func){
    promise.then(func);
    return promise;
  };
  promise.cancel = function(func){
    promise.then(null, func);
    return promise;
  };
  return promise;
};

$modal.open(item)
.ok(function(data){ // ok逻辑 })
.cancel(function(data){ // cancel 逻辑 });

promise.success = function(fn) {
  promise.then(function(response) {
    fn(response.data, response.status, response.headers, config); });
    return promise;
  };
  promise.error = function(fn) {
    promise.then(null, function(response) {
    fn(response.data, response.status, response.headers, config);
  });
  return promise;
};
1
// 注册一个拦截器服务
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
  return {
      // 可选方法
      'request': function(config) {
          // 请求成功后处理
          return config;
      },
      // 可选方法
      'requestError': function(rejection) {
          // 请求失败后的处理
          if (canRecover(rejection)) {
            return responseOrNewPromise
          }
            return $q.reject(rejection);
      },
      // 可选方法
      'response': function(response) {
          // 返回回城处理
          return response;
      },
      // 可选方法
      'responseError': function(rejection) {
          // 返回失败的处理
          if (canRecover(rejection)) {
          return responseOrNewPromise
      }
      return $q.reject(rejection);
      }
  };
});
// 将服务注册到拦截器链中
$httpProvider.interceptors.push('myHttpInterceptor');
// 同样也可以将拦截器注册为一个工厂方法。 但上一中方式更为推荐。
$httpProvider.interceptors.push(['$q', function($q) {
   return { 'request': function(config) { // 同上 },
            'response': function(response) { // 同上 }
   };
 }]);

这样就可以实现对Angular中的$http或者是$resource的Ajax请求拦截了。但在Angular内部是是如何实现这种拦截方式的呢?Angular使用的就是Promise机制,形成异步管道流,将真实的Ajax请求放置在request、requestError和response、responseError的管道中间,因此就产生了对Ajax请求的拦截。

其源码实现如下:

1
var interceptorFactories = this.interceptors = [];
var responseInterceptorFactories = this.responseInterceptors = [];
this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
  function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
    var defaultCache = $cacheFactory('$http');
    var reversedInterceptors = [];
    forEach(interceptorFactories, function(interceptorFactory) {
      reversedInterceptors.unshift(isString(interceptorFactory) ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
    });
    forEach(responseInterceptorFactories, function(interceptorFactory, index) {
      var responseFn = isString(interceptorFactory) ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory);
      reversedInterceptors.splice(index, 0, {
        response: function(response) {
          return responseFn($q.when(response));
        },
        responseError: function(response) {
          return responseFn($q.reject(response));
        }
      });
    });
    ...
function $http(requestConfig) {
  ...
  var chain = [serverRequest, undefined];
  var promise = $q.when(config);
  // apply interceptors
  forEach(reversedInterceptors, function(interceptor) {
    if (interceptor.request || interceptor.requestError) {
      chain.unshift(interceptor.request, interceptor.requestError);
    }
    if (interceptor.response || interceptor.responseError) {
      chain.push(interceptor.response, interceptor.responseError);
    }
  });
  while (chain.length) {
    var thenFn = chain.shift();
    var rejectFn = chain.shift();

    promise = promise.then(thenFn, rejectFn);
  }
  promise.success = function(fn) {
    promise.then(function(response) {
      fn(response.data, response.status, response.headers, config);
    });
    return promise;
  };
  promise.error = function(fn) {
    promise.then(null, function(response) {
      fn(response.data, response.status, response.headers, config);
    });
    return promise;
  };

  return promise;
};