您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

如何让node中promise替代回调函数

2025/1/14 17:45:25发布12次查看
这次给大家带来如何让node中promise替代回调函数,如何让node中promise替代回调函数的注意事项有哪些,下面就是实战案例,一起来看一下。
在学习 node.js 过程中接触到了如何使用 async 来控制并发(使用 async 控制并发)
async 的本质是一个流程控制。其实在异步编程中,还有一个更为经典的模型,叫做 promise/deferred 模型(当然还有更多相关解决方法,比如 eventproxy,co 等,到时候遇到在挖坑)
首先,我们思考一个典型的异步编程模型,考虑这样一个题目:读取一个文件,在控制台输出这个文件内容
var fs = require('fs'); fs.readfile('1.txt', 'utf8', function (err, data) {   console.log(data); });
看起来很简单,再进一步: 读取两个文件,在控制台输出这两个文件内容
var fs = require('fs'); fs.readfile('1.txt', 'utf8', function (err, data) {   console.log(data);   fs.readfile('2.txt', 'utf8', function (err, data) {     console.log(data);   }); });
要是读取更多的文件呢
var fs = require('fs'); fs.readfile('1.txt', 'utf8', function (err, data) {   fs.readfile('2.txt', 'utf8', function (err, data) {     fs.readfile('3.txt', 'utf8', function (err, data) {       fs.readfile('4.txt', 'utf8', function (err, data) {         // ...       });     });   }); });
这就是传说中的 callback hell,可以使用 async 来改善这段代码,但是在本例中我们要用 promise/defer 来改善它
promise 基本概念
首先它是一个对象,它和 javascript 普通的对象没什么区别,同时,它也是一种规范,跟异步操作约定了统一的接口,表示一个异步操作的最终结果,以同步的方式来写代码,执行的操作是异步的,但又保证程序执行的顺序是同步的
1. promise 只有三种状态,未完成,完成 (fulfilled) 和失败 (rejected)
2. promise 的状态可以由未完成转换成完成,或者未完成转换成失败
3. promise 的状态转换只发生一次
promise 有一个 then 方法,then 方法可以接受 3 个函数作为参数。前两个函数对应 promise 的两种状态 fulfilled, rejected 的回调函数。第三个函数用于处理进度信息
为了理解它,一些重要原理必须记牢:.then() 总是返回一个新的 promise,如下面代码:
var promise = readfile() var promise2 = promise.then(readanotherfile, console.error)
这里 then 的参数 readanotherfile, console.error 是代表异步操作成功后的动作 onfulfilled 或失败后的动作 onrejected,也就是说,读取文件成功后执行 readanotherfile 函数,否则失败打印记录错误。这种实现是两个中只有一种可能
也可以理解为:
promisesomething().then(function (fulfilled) {   // 当 promise 状态变成 fulfilled 时,调用此函数 }, function (rejected) {   // 当 promise 状态变成 rejected 时,调用此函数 }, function (progress) {   // 当返回进度信息时,调用此函数 });
promise 法则有两部分必须分离:
1. then() 总是返回一个新的 promise,每次你调用它,它不管回调做什么,因为 .then() 在回调被调用之前已经给了你一个承诺 promise,回调的行为只影响承诺 promise 的实施,如果回调返回一个值,那么 promise 将使用那个值,如果这个值是一个 promise,返回这个 promise 实施后的值给这个值,如果回调抛出错误,promise 将拒绝错误
2. 被 .then() 返回的 promise 是一个新的 promise ,它不同于那些 .then() 被调用的 promise,promise 长长的链条有时会好些隐藏这个事实,不管如何,每次 .then() 调用都会产生一个新的 promise,这里必须注意的是你真正需要考虑的是你最后调用 .then() 可能代表失败,那么如果你不捕获这种失败,那么容易导致你的错误 exception 消失
来看一个利用 q 来处理这种问题的简单例子:
var q = require('q'); var defer = q.defer(); /**  * 获取初始 promise  * @private  */ function getinitialpromise() {   return defer.promise; }   /**  * 为 promise 设置三种状态的回调函数  */ getinitialpromise().then(function (success) {   console.log(success); }, function (error) {   console.log(error); }, function (progress) {   console.log(progress); }); defer.notify('in progress'); // 控制台打印 in progress defer.resolve('resolve');   // 控制台打印 resolve defer.reject('reject');    // 没有输出。promise 的状态只能改变一次
promise 的传递
then 方法会返回一个 promise,在下面这个例子中,我们用 outputpromise 指向 then 返回的 promise。
var outputpromise = getinputpromise().then(function (fulfilled) {     }, function (rejected) {     });
现在 outputpromise 就变成了受 function(fulfilled) 或者 function(rejected) 控制状态的 promise 了。直白的意思就是:当 function(fulfilled) 或者 function(rejected) 返回一个值,比如一个字符串,数组,对象等等,那么 outputpromise 的状态就会变成 fulfilled。
在下面这个例子中,我们可以看到,当我们把 inputpromise 的状态通过 defer.resovle() 变成 fulfilled 时,控制台输出 fulfilled.
当我们把 inputpromise 的状态通过 defer.reject() 变成 rejected,控制台输出 rejected
var q = require('q'); var defer = q.defer(); /**  * 通过 defer 获得 promise  * @private  */ function getinputpromise() {   return defer.promise; }   /**  * 当 inputpromise 状态由未完成变成 fulfil 时,调用 function(fulfilled)  * 当 inputpromise 状态由未完成变成 rejected 时,调用 function(rejected)  * 将 then 返回的 promise 赋给 outputpromise  * function(fulfilled) 和 function(rejected) 通过返回字符串将 outputpromise 的状态由  * 未完成改变为 fulfilled  * @private  */ var outputpromise = getinputpromise().then(function (fulfilled) {   return 'fulfilled'; }, function (rejected) {   return 'rejected'; });   /**  * 当 outputpromise 状态由未完成变成 fulfil 时,调用 function(fulfilled),控制台打印 'fulfilled: fulfilled'。  * 当 outputpromise 状态由未完成变成 rejected, 调用 function(rejected), 控制台打印 'rejected: rejected'。  */ outputpromise.then(function (fulfilled) {   console.log('fulfilled: ' + fulfilled); }, function (rejected) {   console.log('rejected: ' + rejected); });   /**  * 将 inputpromise 的状态由未完成变成 rejected  */ defer.reject(); // 输出 fulfilled: rejected   /**  * 将 inputpromise 的状态由未完成变成 fulfilled  */ //defer.resolve(); // 输出 fulfilled: fulfilled
当 function(fulfilled) 或者 function(rejected) 抛出异常时,那么 outputpromise 的状态就会变成 rejected
var q = require('q'); var fs = require('fs'); var defer = q.defer();   /**  * 通过 defer 获得 promise  * @private  */ function getinputpromise() {   return defer.promise; }   /**  * 当 inputpromise 状态由未完成变成 fulfil 时,调用 function(fulfilled)  * 当 inputpromise 状态由未完成变成 rejected 时,调用 function(rejected)  * 将 then 返回的 promise 赋给 outputpromise  * function(fulfilled) 和 function(rejected) 通过抛出异常将 outputpromise 的状态由  * 未完成改变为 reject  * @private  */ var outputpromise = getinputpromise().then(function (fulfilled) {   throw new error('fulfilled'); }, function (rejected) {   throw new error('rejected'); });   /**  * 当 outputpromise 状态由未完成变成 fulfil 时,调用 function(fulfilled)。  * 当 outputpromise 状态由未完成变成 rejected, 调用 function(rejected)。  */ outputpromise.then(function (fulfilled) {   console.log('fulfilled: ' + fulfilled); }, function (rejected) {   console.log('rejected: ' + rejected); });   /**  * 将 inputpromise 的状态由未完成变成 rejected  */ defer.reject();   // 控制台打印 rejected [error:rejected]   /**  * 将 inputpromise 的状态由未完成变成 fulfilled  */ //defer.resolve(); // 控制台打印 rejected [error:fulfilled]
当 function(fulfilled) 或者 function(rejected) 返回一个 promise 时,outputpromise 就会成为这个新的 promise.
这样做的意义在于聚合结果 (q.all),管理延时,异常恢复等等
比如说我们想要读取一个文件的内容,然后把这些内容打印出来。可能会写出这样的代码:
// 错误的写法 var outputpromise = getinputpromise().then(function (fulfilled) {   fs.readfile('test.txt', 'utf8', function (err, data) {     return data;   }); });
然而这样写是错误的,因为 function(fulfilled) 并没有返回任何值。需要下面的方式:
var q = require('q'); var fs = require('fs'); var defer = q.defer();   /**  * 通过 defer 获得promise  * @private  */ function getinputpromise() {   return defer.promise; }   /**  * 当 inputpromise 状态由未完成变成 fulfil时,调用 function(fulfilled)  * 当 inputpromise 状态由未完成变成 rejected时,调用 function(rejected)  * 将 then 返回的 promise 赋给 outputpromise  * function(fulfilled) 将新的 promise 赋给 outputpromise  * 未完成改变为 reject  * @private  */ var outputpromise = getinputpromise().then(function (fulfilled) {   var mydefer = q.defer();   fs.readfile('test.txt', 'utf8', function (err, data) {     if (!err && data) {       mydefer.resolve(data);     }   });   return mydefer.promise; }, function (rejected) {   throw new error('rejected'); });   /**  * 当 outputpromise 状态由未完成变成 fulfil 时,调用 function(fulfilled),控制台打印 test.txt 文件内容。  *  */ outputpromise.then(function (fulfilled) {   console.log(fulfilled); }, function (rejected) {   console.log(rejected); });   /**  * 将 inputpromise 的状态由未完成变成 rejected  */ //defer.reject();   /**  * 将 inputpromise 的状态由未完成变成 fulfilled  */ defer.resolve(); // 控制台打印出 test.txt 的内容
方法传递
方法传递有些类似于 java 中的 try 和 catch。当一个异常没有响应的捕获时,这个异常会接着往下传递
方法传递的含义是当一个状态没有响应的回调函数,就会沿着 then 往下找
没有提供 function(rejected)
var outputpromise = getinputpromise().then(function (fulfilled) { })
如果 inputpromise 的状态由未完成变成 rejected, 此时对 rejected 的处理会由 outputpromise 来完成
var q = require('q'); var fs = require('fs'); var defer = q.defer(); /**  * 通过defer获得promise  * @private  */ function getinputpromise() {   return defer.promise; }   /**  * 当 inputpromise 状态由未完成变成 fulfil 时,调用 function(fulfilled)  * 当 inputpromise 状态由未完成变成 rejected 时,这个 rejected 会传向 outputpromise  */ var outputpromise = getinputpromise().then(function (fulfilled) {   return 'fulfilled' }); outputpromise.then(function (fulfilled) {   console.log('fulfilled: ' + fulfilled); }, function (rejected) {   console.log('rejected: ' + rejected); });   /**  * 将 inputpromise 的状态由未完成变成 rejected  */ defer.reject('inputpromise rejected'); // 控制台打印 rejected: inputpromise rejected   /**  * 将 inputpromise的状态由未完成变成fulfilled  */ //defer.resolve();
没有提供 function(fulfilled)
var outputpromise = getinputpromise().then(null, function (rejected) { })
如果 inputpromise 的状态由未完成变成 fulfilled, 此时对 fulfil 的处理会由 outputpromise 来完成
var q = require('q'); var fs = require('fs'); var defer = q.defer();   /**  * 通过defer获得promise  * @private  */ function getinputpromise() {   return defer.promise; }   /**  * 当 inputpromise 状态由未完成变成 fulfil时,传递给 outputpromise  * 当 inputpromise 状态由未完成变成 rejected时,调用 function(rejected)  * function(fulfilled) 将新的 promise 赋给 outputpromise  * 未完成改变为 reject  * @private  */ var outputpromise = getinputpromise().then(null, function (rejected) {   return 'rejected'; });   outputpromise.then(function (fulfilled) {   console.log('fulfilled: ' + fulfilled); }, function (rejected) {   console.log('rejected: ' + rejected); });   /**  * 将 inputpromise 的状态由未完成变成 rejected  */ // defer.reject('inputpromise rejected');   /**  * 将 inputpromise 的状态由未完成变成fulfilled  */ defer.resolve('inputpromise fulfilled'); // 控制台打印fulfilled: inputpromise fulfilled
可以使用 fail(function(error)) 来专门针对错误处理,而不是使用 then(null,function(error))
var outputpromise = getinputpromise().fail(function (error) { })
看这个例子:
var q = require('q'); var fs = require('fs'); var defer = q.defer(); /**  * 通过defer获得promise  * @private  */ function getinputpromise() {   return defer.promise; }   /**  * 当 inputpromise 状态由未完成变成 fulfil 时,调用 then(function(fulfilled))  * 当 inputpromise 状态由未完成变成 rejected 时,调用 fail(function(error))  * function(fulfilled) 将新的 promise 赋给 outputpromise  * 未完成改变为reject  * @private  */ var outputpromise = getinputpromise().then(function (fulfilled) {   return fulfilled; }).fail(function (error) {   console.log('fail: ' + error); }); /**  * 将 inputpromise 的状态由未完成变成 rejected  */ defer.reject('inputpromise rejected');// 控制台打印 fail: inputpromise rejected   /**  * 将 inputpromise 的状态由未完成变成 fulfilled  */ //defer.resolve('inputpromise fulfilled');
可以使用 progress(function (progress)) 来专门针对进度信息进行处理,而不是使用 then(function (success) { }, function (error) { }, function (progress) { })
var q = require('q'); var defer = q.defer(); /**  * 获取初始 promise  * @private  */ function getinitialpromise() {   return defer.promise; } /**  * 为 promise 设置 progress 信息处理函数  */ var outputpromise = getinitialpromise().then(function (success) {   }).progress(function (progress) {   console.log(progress); });   defer.notify(1); defer.notify(2); // 控制台打印 1,2
promise 链
promise 链提供了一种让函数顺序执行的方法
函数顺序执行是很重要的一个功能。比如知道用户名,需要根据用户名从数据库中找到相应的用户,然后将用户信息传给下一个函数进行处理
var q = require('q'); var defer = q.defer(); // 一个模拟数据库 var users = [{ 'name': 'andrew', 'passwd': 'password' }]; function getusername() {   return defer.promise; }   function getuser(username) {   var user;   users.foreach(function (element) {     if (element.name === username) {       user = element;     }   });   return user; }   // promise 链 getusername().then(function (username) {   return getuser(username); }).then(function (user) {   console.log(user); });   defer.resolve('andrew');
我们通过两个 then 达到让函数顺序执行的目的。
then 的数量其实是没有限制的。当然,then 的数量过多,要手动把他们链接起来是很麻烦的。比如
foo(initialval).then(bar).then(baz).then(qux)
这时我们需要用代码来动态制造 promise 链
var funcs = [foo, bar, baz, qux] var result = q(initialval)  funcs.foreach(function (func) {   result = result.then(func) }) return result
当然,我们可以再简洁一点
var funcs = [foo, bar, baz, qux] funcs.reduce(function (pre, current),q(initialval){   return pre.then(current) })
看一个具体的例子
function foo(result) {   console.log(result);   return result + result; }   // 手动链接 q('hello').then(foo).then(foo).then(foo);   // 控制台输出: hello // hellohello // hellohellohello   // 动态链接 var funcs = [foo, foo, foo]; var result = q('hello');   funcs.foreach(function (func) {   result = result.then(func); });   // 精简后的动态链接 funcs.reduce(function (prev, current) {   return prev.then(current); }, q('hello'));
对于 promise 链,最重要的是需要理解为什么这个链能够顺序执行。如果能够理解这点,那么以后自己写 promise 链可以说是轻车熟路啊
promise 组合
回到我们一开始读取文件内容的例子。如果现在让我们把它改写成 promise 链,是不是很简单呢?
var q = require('q'),   fs = require('fs');   function printfilecontent(filename) {   return function () {     var defer = q.defer();     fs.readfile(filename, 'utf8', function (err, data) {       if (!err && data) {         console.log(data);         defer.resolve();       }     })     return defer.promise;   } }   // 手动链接 printfilecontent('sample01.txt')()   .then(printfilecontent('sample02.txt'))   .then(printfilecontent('sample03.txt'))   .then(printfilecontent('sample04.txt'));  // 控制台顺序打印 sample01 到 sample04 的内容
很有成就感是不是。然而如果仔细分析,我们会发现为什么要他们顺序执行呢,如果他们能够并行执行不是更好吗? 我们只需要在他们都执行完成之后,得到他们的执行结果就可以了
我们可以通过 q.all([promise1,promise2...]) 将多个 promise 组合成一个 promise 返回。 注意:
1. 当 all 里面所有的 promise 都 fulfil 时,q.all 返回的 promise 状态变成 fulfil
2. 当任意一个 promise 被 reject 时,q.all 返回的 promise 状态立即变成 reject
我们来把上面读取文件内容的例子改成并行执行吧
var q = require('q'); var fs = require('fs'); /**  *读取文件内容  *@private  */ function printfilecontent(filename) {   // todo: 这段代码不够简洁。可以使用 q.denodeify 来简化   var defer = q.defer();     fs.readfile(filename, 'utf8', function (err, data) {     if (!err && data) {       console.log(data);       defer.resolve(filename + ' success ');     } else {       defer.reject(filename + ' fail ');     }   })     return defer.promise;   }   q.all([printfilecontent('sample01.txt'), printfilecontent('sample02.txt'), printfilecontent('sample03.txt'), printfilecontent('sample04.txt')])   .then(function (success) {     console.log(success);   }); // 控制台打印各个文件内容 顺序不一定
现在知道 q.all 会在任意一个 promise 进入 reject 状态后立即进入 reject 状态。如果我们需要等到所有的 promise 都发生状态后(有的 fulfil, 有的 reject),再转换 q.all 的状态, 这时我们可以使用 q.allsettled
var q = require('q'),   fs = require('fs'); /**  *读取文件内容  *@private  */   function printfilecontent(filename) {     // todo: 这段代码不够简洁。可以使用q.denodeify来简化   var defer = q.defer();     fs.readfile(filename, 'utf8', function (err, data) {     if (!err && data) {       console.log(data);       defer.resolve(filename + ' success ');     } else {       defer.reject(filename + ' fail ');     }   })     return defer.promise;     }   q.allsettled([printfilecontent('nosuchfile.txt'), printfilecontent('sample02.txt'), printfilecontent('sample03.txt'), printfilecontent('sample04.txt')])   .then(function (results) {     results.foreach(       function (result) {         console.log(result.state);       }     );   });
结束 promise 链
通常,对于一个 promise 链,有两种结束的方式。第一种方式是返回最后一个 promise
如 return foo().then(bar);
第二种方式就是通过 done 来结束 promise 链
如 foo().then(bar).done()
为什么需要通过 done 来结束一个 promise 链呢? 如果在我们的链中有错误没有被处理,那么在一个正确结束的 promise 链中,这个没被处理的错误会通过异常抛出
var q = require('q');  function getpromise(msg, timeout, opt) {    var defer = q.defer();    settimeout(function () {     console.log(msg);     if (opt)       defer.reject(msg);     else       defer.resolve(msg);   }, timeout);    return defer.promise;  }   /**  * 没有用 done() 结束的 promise 链  * 由于 getpromse('2',2000,'opt') 返回 rejected, getpromise('3',1000) 就没有执行  * 然后这个异常并没有任何提醒,是一个潜在的 bug  */ getpromise('1', 3000)   .then(function () { return getpromise('2', 2000, 'opt') })   .then(function () { return getpromise('3', 1000) });   /**  * 用 done() 结束的 promise 链  * 有异常抛出  */ getpromise('1', 3000)   .then(function () { return getpromise('2', 2000, 'opt') })   .then(function () { return getpromise('3', 1000) })   .done();
附录:一个 promise 简单的应用(node.js笔记(5)promise)
附:promises/a+ 规范
promise 代表一个异步操作的最终结果。主要通过 promise 的 then 方法订阅其最终结果的处理回调函数,和订阅因某原因无法成功获取最终结果的处理回调函数。
更对详细见:promises/a+
a 与 a+ 的不同点
a+ 规范通过术语 thenable 来区分 promise 对象
a+ 定义 onfulfilled/onrejectd 必须是作为函数来调用,而且调用过程必须是异步的
a+ 严格定义了 then 方法链式调用时,onfulfilled/onrejectd 的调用顺序
相信看了本文案例你已经掌握了方法,更多精彩请关注其它相关文章!
推荐阅读:
vue2.0实现输入框实时检索更新步骤详解
使用vue.js实现编辑菜谱
以上就是如何让node中promise替代回调函数的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product