怎么用promise实现原理异步控制

怎么用promise实现异步控制_百度知道& & & &感谢支持ayqy个人订阅号,每周义务推送1篇(only unique one)原创精品博文,话题包括但不限于前端、Node、Android、数学(WebGL)、语文(课外书读后感)、英语(文档翻译) & & & &
& & & &如果觉得弱水三千,一瓢太少,可以去 http://blog.ayqy.net 看个痛快
& & & &写在前面铺天盖地的Promise文章还没完全消散,Promise本身就被ES7劝退了关于Promise的具体语法及应用场景,可以查看:完全理解PromisePromise的适用场景至于另一篇(动手实现promise),不建议看,这个实现存在的问题很多(性能、未知bug等等),也不打算继续维护,千万不要用Promise作为一种异步流程控制方法,本篇介绍其实现机制,并模拟实现基本功能一.基础部分Promise内部分为Promise对象与Deferred对象,前者提供接口接收handler,后者记录状态,维持异步逻辑正确执行(男主外女主内的感觉,promise.then()接收handler,内部的deferred负责维护状态)PromisePromise主外,接收handler并注册给事件机制简单情况下,Promise可以直接套用EventEmitter,如下:var EventEmitter = require('events');
var util = require('util');
//--- 自定义promise
var MyPromise = function() {
& &EventEmitter.call(this);
util.inherits(MyPromise, EventEmitter);然后公开then(),负责接收handler:MyPromise.prototype.then = function(onFulfilled, onRejected, onProgress) {
& &if (typeof onFulfilled === 'function') {
& & & &this.once('resolve', onFulfilled);
& &if (typeof onRejected === 'function') {
& & & &this.once('reject', onRejected);
& &if (typeof onProgress === 'function') {
& & & &this.on('progress', onProgress);
};主外的就结束了,完全看不到将要成为Promise的端倪(现在确实不像,因为这个Promise不是最终暴露出去的Promise)DeferredDeferred要维护状态,稍复杂一些初始状态,如下:var Deferred = function() {
& &this.state = 'pending';
& &this.promise = new MyPromise();
};Deferred实例持有promise引用以便触发事件执行handler(主内的管着主外的)然后把事件机制与状态联系起来:Deferred.prototype.resolve = function(res) {
& &this.promise.emit('resolve', res);
& &this.state = 'resolved';
Deferred.prototype.reject = function(err) {
& &this.promise.emit('reject', err);
& &this.state = 'rejected';
Deferred.prototype.progress = function(chunk) {
& &this.promise.emit('progress', chunk);
};最基础的部分完成了,最后提供一个Promise并暴露出去:module.exports = {
& &Promise: MyPromise,
& &Deferred: Deferred,
& &MyPromise: function(fn) {
& & & &// 向fn注入reslove和reject
& & & &var deferred = new Deferred();
& & & &fn.call(deferred, (res) =& {
& & & & & &process.nextTick(() =& {deferred.resolve(res);});
& & & &}, (err) =& {
& & & & & &process.nextTick(() =& {deferred.reject(err);});
& & & &});
& & & &return deferred.
};module.exports.MyPromise就是最终产物,延迟执行reslove/reject是为了等待外部的promise.then(),也就是先让handler同步注册,然后再触发事件,这也是Promise不用预先指定分支及延迟逻辑处理的秘密所在示例如下:var P = require('./p.js');
var p1 = new P.MyPromise(function(resolve, reject) {
& &console.log('#1');
& &resolve(1);
& &console.log('#2');
p1.then((res) =& {console.log(res);});
// log print:
var p2 = new P.MyPromise(function(resolve, reject) {
& &console.log('#1');
& &reject(new Error('reject'));
& &console.log('#2');
p2.then(null, (err) =& {console.log(err.toString());});
// log print:
// Error: reject二.扩展功能1.回调函数生成Node回调函数大都遵循callback(err, res1, res...)的规则,可以据此生成回调函数:// node callback
Deferred.prototype.callback = function() {
& &return (err, value) =& {
& & & &if (err) {
& & & & & &this.reject(err);
& & & &else if (arguments.length & 2) {
& & & & & &this.resolve(Array.prototype.slice.call(arguments, 1));
& & & &else {
& & & & & &this.resolve(value);
};可以简化promisify过程,让Promise更好用一些2.多依赖异步控制各个异步库都提供了依赖控制方法,比如大而全的async模块提供的series(), parallel(), waterfall()等等Promise提供的依赖控制不够多,只支持all(), race(),这里模拟实现all(),如下:// 多依赖异步控制
Deferred.prototype.all = function(promises) {
& &var results = [];
& &var count = promises.
& &promises.forEach((promise, i) =& {
& & & &promise.then((data) =& {
& & & & & &results[i] =
& & & & & &count--;
& & & & & &if (count === 0) {
& & & & & & & &this.resolve(results);
& & & & & &}
& & & &}, (err) =& {
& & & & & &this.reject(err);
& & & &});
& &return this.
};用法示例:// 多依赖异步控制
var fs = require('fs');
// promisify
var readFile = function(file, encoding) {
& &encoding = encoding || 'utf-8';
& &var deferred = new P.Deferred();
& &fs.readFile(file, encoding, deferred.callback());
& &return deferred.
var p1 = readFile('./p.js');
var p2 = readFile('./index.js');
var d1 = new P.Deferred();
d1.all([p1, p2]).then((arrRes) =& {
& &var aRes = arrRes.map((res) =& {
& & & &return res.toString().slice(0, 20);
& &console.log(aRes);
}, (err) =& {
& &console.log(err);
// log print:
// [ 'var EventEmitter = r', 'var P = require(\'./p' ]这里没有把Deferred包装起来,更清晰一些(其实是懒得包装了- -)3.Promise链支持Promise链需要维护一个队列,并根据队列中每个promise的状态手动控制,偷懒的事件机制不再适用了(嗯,需要大改)Promise剔除事件机制,采用任务队列//--- 自定义promise
var MyPromise = function() {
& &// 用于支持promise链
& &this.queue = [];
& &this.isPromise =
MyPromise.prototype.then = function(onFulfilled, onRejected) {
& &// 采用任务队列手动控制,不利用事件机制触发执行了
& &var handler = {};
& &if (typeof onFulfilled === 'function') {
& & & &handler.onFulfilled = onF
& &if (typeof onRejected === 'function') {
& & & &handler.onRejected = onR
& &this.queue.push(handler);
};Deferred手动执行回调,并传递结果var Deferred = function() {
& &this.state = 'pending';
& &this.promise = new MyPromise();
Deferred.prototype.resolve = function(res) {
& &while (handler = this.promise.queue.shift()) {
& & & &if (handler && handler.onFulfilled) {
& & & & & &// 执行肯定回调
& & & & & &var ret = handler.onFulfilled(res);
& & & & & &// 如果肯定回调的返回值为promise,更新this.promise
& & & & & &if (ret && ret.isPromise) {
& & & & & & & &ret.queue = this.promise.
& & & & & & & &this.promise =
& & & & & & & &
& & & & & &}
Deferred.prototype.reject = function(err) {
& &while (handler = this.promise.queue.shift()) {
& & & &if (handler && handler.onRejected) {
& & & & & &var ret = handler.onRejected(err);
& & & & & &if (ret && ret.isPromise) {
& & & & & & & &ret.queue = this.promise.
& & & & & & & &this.promise =
& & & & & & & &
& & & & & &}
& & & & & &else {
& & & & & & & &// 把ret传入下一个handler的onFulfilled
& & & & & & & &var nextHandler = this.promise.queue.shift();
& & & & & & & &if (nextHandler && nextHandler.onFulfilled) {
& & & & & & & & & &nextHandler.onFulfilled(ret);
& & & & & & & &}
& & & & & & & &
& & & & & &}
};用法示例如下:// promise链
var NewP = require('./newp.js');
// promisify
var readFile = function(file, encoding) {
& &encoding = encoding || 'utf-8';
& &var deferred = new NewP.Deferred();
& &fs.readFile(file, encoding, deferred.callback());
& &return deferred.
// test resolve
readFile('./p.js').then((data) =& {
& &return readFile('./index.js');
}).then((data) =& {
& &console.log('index: ' + data.slice(0, 20));
// log print:
// index: var P = require('./p
// test reject
readFile('./a.bcd').then((data) =& {
& &return readFile('./p.js');
}, (err) =& {
& &console.log('!!!error occurs: ' + err);
& &return 'something for next onFulfilled';
}).then((data) =& {
& &console.log(data);
// log print:
// !!!error occurs: Error: ENOENT: no such file or directory, open...
// something for next onFulfilled4.smoothsmooth表示promisify现有API,技巧是篡改arguments,如下:var smooth = (method) =& {
& &return function() {
& & & &var deferred = new P.Deferred();
& & & &var args = Array.prototype.slice.call(arguments, 0);
& & & &args.push(deferred.callback());
& & & &method.apply(null, args);
& & & &return deferred.
};由smooth内部偷偷提供callback,用法很简洁:var readFile = smooth(fs.readFile);
readFile('./index.js', 'utf-8').then((res) =& {
& &console.log(res.slice(0, 20));
});异步方法的回调函数从代码中消失了。这个技巧对于“正版Promise”同样适用,需要用Promise包装大量API时可以考虑三.总结Promise在舞台上转了一圈,然后没能登上领奖台作为异步流程控制方案,Promise最大的问题就是存在封装(promisify)成本,而且提供的多依赖控制方法不够用,需要自行扩展,所以,不好用,不好用就不会长久过时了就没什么好说的,还记得当时铺天盖地的《关于Promise你不知道的…》、《你真的会用Promise吗》参考资料《深入浅出NodeJS》联系ayqy
& & &如果在文章中发现了什么问题,请查看原文并留下评论,ayqy看到就会回复的(不建议直接回复公众号,看不到的啦)特别要紧的问题,可以直接微信联系ayqywx
& & &ayqy(gh_690b43d4ba22) 
 文章为作者独立观点,不代表微头条立场
的最新文章
如果跟着全文一步一步从ES5走过来,那么没有理由排斥ES6的class,因为它解决了一直以来存在的很多问题,比如“应该在哪里声明什么”和“更优雅的继承实现”let是更完美的var,但用好let同样不容易,不只是作为ES6起手式那么简单如果能够带来高可维护性,长一点丑一点麻烦一点又有什么关系呢?每天都忙,却记不清在忙些什么Step更像是个魔术,库还能这么设计?Promise在舞台上转了一圈,然后没能登上领奖台AOP是对OOP的补充,横向切入对象并进行逻辑注入,确保类的职责单一JS要变Java了!?清爽的类型定义语法,keep it simpleBEM是什么,有什么用,怎么用reset?normalize?需要吗?不需要吗?es6的基本语法糖,简单易用根据FireFox中该特性的实现者的亲述,补充一些细节和应用场景动手实现WordPress插件,把博文自动转换为微信公众平台的图文消息怎么说呢,快速开发很棒,后遗症很伤。全面了解GLSL ES语言,包括语法规则、数据类型、流程控制、函数等等Web Workers是什么,有什么用,怎么用通过attribute变量向顶点着色器中传值,我们就可以动态修改点的位置了gh_690b43d4ba22黯羽轻扬热门文章最新文章gh_690b43d4ba22黯羽轻扬Javascript异步编程模型Promise模式详细介绍
字体:[ ] 类型:转载 时间:
异步模式在 Web 编程中变得越来越重要,如何处理异步请求后的操作是一件麻烦事。Promise 是一种异步编程模型,术语称作 Deferred 模式,它通过一组API来规范化异步操作,让异步操作的流程控制更加容易。
Promise 编程模式也被称为 thenable,可以理解为 延迟后执行。每个 Promise 都拥有一个叫做 then 的唯一接口,当 Promise 失败或成功时,它就会进行回调。它代表了一种可能会长时间运行而且不一定必须完成的操作结果。这种模式不会阻塞和等待长时间的操作完成,而是返回一个代表了承诺的(promised)结果的对象。
当前的许多 JavaScript 库(如 jQuery 和 Dojo、AngularJS)均添加了这种称为 Promise 的抽象。通过这些库,开发人员能够在实际编程中使用 Promise 模式。
下面我们将以 jQuery 为例讨论 JavaScript 库是如何使用 Promise 模式的来处理异步的,其实就是通过回调的方式提供容错支持。在某个操作成功或失败或任何情况下都执行对应的回调,尽量把某段逻辑可能出现的情况都 handle 住。
首先让我们来看看 jQuery 一般是如何操作的:
代码如下:var $info = $("#info");$.ajax({&&& url:"/echo/json/",&&& data: { json: JSON.stringify({"name": "someValue"}) },&&& type:"POST",&&& success: function(response)&&& {&&&&&& $info.text(response.name);&&& }});
在这个例子中,你可以看到当设置成功后会指定一个回调,是一种很好的回调方式,这并不是 Promise,jQuery 官方文档也不再推荐这种方式(/jQuery.ajax/#jqXHR)。 当 Ajax 调用完成后,它便会执行 success 函数。根据库所使用的异步操作,你可以使用各种不同的回调(即任务是否成功,都会进行回调,做出响应)。使用 Promise 模式会简化这个过程,异步操作只需返回一个对象调用。这个 Promise 允许你调用一个叫做 then 的方法,然后让你指定回调的 function(s) 个数。
下面让我们来看看 jQuery 是如何建立 Promise 的: 代码如下:var $info = $("#info");$.ajax({&&& url: "/echo/json/",&&& data: {&&&&&&& json: JSON.stringify({&&&&&&&&&&& "name": "someValue"&&&&&&& })&&& },&&& type: "POST"}).then(function (response) {&&& $info.text(response.name);});
jQuery ajax 对象通过返回 xhr 对象实现 Promise 模式,所以我们可以调用 then 方法,这样做的优势是你可以链式调用,实现独立操作,如下所示 :
代码如下:var $info = $("#info");$.ajax({&&& url: "/echo/json/",&&& data: {&&&&&&& json: JSON.stringify({&&&&&&&&&&& "name": "someValue"&&&&&&& })&&& },&&& type: "POST"}).then(function (response) {&&& $info.text(response.name);}).then(function () {&&& $info.append("...More");}).done(function () {&&& $info.append("...finally!");});
由于许多库都开始采用 Promise 模式,所以异步操作会变的非常容易。但如果站在相反的角度思考,Promise 将会是什么样子的呢?其中一个非常重要的模式是函数可以接受两种功能,一个是成功时的回调,另一个是失败时的回调。
代码如下:var $info = $("#info");
$.ajax({// Change URL to see error happen&&& url: "/echo/json/",&&& data: {&&&&&&& json: JSON.stringify({&&&&&&&&&&& "name": "someValue"&&&&&&& })&&& },&&& type: "POST"}).then(function (response) {// success&&& $info.text(response.name);},function () {// failure&&& $info.text("bad things happen to good developers");}).always(function () {&&& $info.append("...finally");});
需要注意的是,在 jQuery 里,无论成功还是失败,我们都会使用一个调用来指定我们想要调用的。其实这里也可以这样来写,这也是 jQuery 官方文档里推荐的方法:
代码如下:var $info = $("#info");
$.ajax({// Change URL to see error happen&&& url: "/echo/json/",&&& data: {&&&&&&& json: JSON.stringify({&&&&&&&&&&& "name": "someValue"&&&&&&& })&&& },&&& type: "POST"}).done(function (response) {& // success& $info.text(response.name);}).fail(function () {& // failure& $info.text("bad things happen to good developers");}).always(function () {&&& $info.append("...finally");});
下面再来看看 AngularJS 是如何使用 Promise 模式的:
代码如下:var m = angular.module("myApp", []);
m.factory("dataService", function ($q) {&&& function _callMe() {&&&&&&& var d = $q.defer();&&&&&&& setTimeout(function () {&&&&&&&&&&& d.resolve();&&&&&&&&&&& //defer.reject();&&&&&&& }, 100);&&&&&&& return d.&&& }&&& return {&&&&&&& callMe: _callMe&&& };});
function myCtrl($scope, dataService) {&&& $scope.name = "None";&&& $scope.isBusy =&&& dataService.callMe()&&&&& .then(function () {&&&&&&& // Successful&&&&&&& $scope.name = "success";&&&&& },&&&&& function () {&&&&&&& // failure&&&&&&& $scope.name = "failure";&&&&& })&&&&& .then(function () {&&&&&&& // Like a Finally Clause&&&&&&& $scope.isBusy =&&&&& });}
你可以在 JSFiddle 里试试这些例子,并且看看会产生哪些效果。使用 Promise 来操作异步是一种非常简单的方式,而且还可以简化你的代码,确实是一举两得的好方法。更多关于Promise的介绍及示例,可以前往官网(http://www.promisejs.org/)查看。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具怎么用promise实现异步控制_百度知道

我要回帖

更多关于 promise js 实现 的文章

 

随机推荐