ノードの操作方法とコールバック関数の代わりにPromiseを使用する方法
今回はノードでコールバック関数の代わりにPromiseを使う方法を紹介します。ノードでコールバック関数の代わりにPromiseを使うときの注意点を実際に見てみましょう。
async の本質は まず、考えてみましょう 典型的な非同期プログラミング モデルについて考えてみましょう: ファイルを読み取り、ファイルの内容をコンソールに出力します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) { // ... }); }); }); });
var promise = readFile() var promise2 = promise.then(readAnotherFile, console.error)
promiseSomething().then(function (fulfilled) { // 当 promise 状态变成 fulfilled 时,调用此函数 }, function (rejected) { // 当 promise 状态变成 rejected 时,调用此函数 }, function (progress) { // 当返回进度信息时,调用此函数 });
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) { });
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 的调用顺序
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上がノードの操作方法とコールバック関数の代わりにPromiseを使用する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









PyCharm は非常に人気のある Python 統合開発環境 (IDE) であり、Python 開発をより効率的かつ便利にするための豊富な機能とツールを提供します。この記事では、PyCharm の基本的な操作方法を紹介し、読者がすぐに使い始めてツールの操作に習熟できるように、具体的なコード例を示します。 1. PyCharm をダウンロードしてインストールします。 まず、PyCharm 公式 Web サイト (https://www.jetbrains.com/pyc) にアクセスする必要があります。

sudo (スーパーユーザー実行) は、一般ユーザーが root 権限で特定のコマンドを実行できるようにする、Linux および Unix システムの重要なコマンドです。 sudo の機能は主に次の側面に反映されています。 権限制御の提供: sudo は、ユーザーにスーパーユーザー権限を一時的に取得することを許可することで、システム リソースと機密性の高い操作を厳密に制御します。一般のユーザーは、必要な場合にのみ sudo を介して一時的な権限を取得できるため、常にスーパーユーザーとしてログインする必要はありません。セキュリティの向上: sudo を使用すると、日常的な操作中に root アカウントの使用を回避できます。すべての操作に root アカウントを使用すると、誤った操作や不注意な操作には完全な権限が与えられるため、予期しないシステムの損傷につながる可能性があります。そして

LinuxDeploy の操作手順と注意事項 LinuxDeploy は、ユーザーが Android デバイスにさまざまな Linux ディストリビューションを迅速に展開できるようにする強力なツールで、ユーザーはモバイル デバイスで完全な Linux システムを体験できます。この記事では、LinuxDeploy の操作手順と注意事項を詳しく紹介し、読者がこのツールをより効果的に使用できるように、具体的なコード例を示します。操作手順: Linux のインストールDeploy: まず、インストールします

おそらく多くのユーザーは、自宅に未使用のコンピュータを複数台持っており、長期間使用していなかったためにパワーオン パスワードを完全に忘れてしまったため、パスワードを忘れた場合の対処方法を知りたいと考えています。それでは、一緒に見てみましょう。 win10 起動パスワードの F2 キーを押し忘れた場合の対処方法 1. コンピューターの電源ボタンを押し、コンピューターの電源を入れるときに F2 キーを押します (コンピューターのブランドによって、BIOS に入るボタンが異なります)。 2. BIOS インターフェイスで、セキュリティ オプションを見つけます (コンピューターのブランドによって場所が異なる場合があります)。通常は上部の設定メニューにあります。 3. 次に、「SupervisorPassword」オプションを見つけてクリックします。 4. この時点で、ユーザーは自分のパスワードを確認できると同時に、その横にある [有効] を見つけて [無効] に切り替えることができます。

スマートフォンの普及に伴い、スクリーンショット機能は携帯電話を日常的に使用する上で必須のスキルの 1 つになりました。 Huaweiの主力携帯電話の1つであるHuawei Mate60Proのスクリーンショット機能は、当然のことながらユーザーの注目を集めています。今日は、誰もがより便利にスクリーンショットを撮れるように、Huawei Mate60Pro携帯電話のスクリーンショットの操作手順を共有します。まず、Huawei Mate60Pro携帯電話はさまざまなスクリーンショット方法を提供しており、個人の習慣に応じて自分に合った方法を選択できます。以下は、一般的に使用されるいくつかのインターセプトの詳細な紹介です。

日常生活では、約束と履行の間で問題に遭遇することがよくあります。個人的な関係でもビジネス取引でも、約束を守ることが信頼を築く鍵となります。ただし、コミットメントの是非についてはしばしば議論の余地があります。この記事では、約束の長所と短所を検討し、約束を守る方法についていくつかのアドバイスを提供します。約束されたメリットは明らかです。まず、コミットメントは信頼を築きます。人が約束を守るとき、その人は信頼できる人であると他人に信じ込ませます。信頼は人々の間に確立される絆であり、それは人々をより良くすることができます

ピン張りのノードの詳細な説明とインストールガイドこの記事では、ピネットワークのエコシステムを詳細に紹介します - PIノードは、ピン系生態系における重要な役割であり、設置と構成の完全な手順を提供します。 Pinetworkブロックチェーンテストネットワークの発売後、PIノードは多くの先駆者の重要な部分になり、テストに積極的に参加し、今後のメインネットワークリリースの準備をしています。まだピン張りのものがわからない場合は、ピコインとは何かを参照してください。リストの価格はいくらですか? PIの使用、マイニング、セキュリティ分析。パインワークとは何ですか?ピン競技プロジェクトは2019年に開始され、独占的な暗号通貨PIコインを所有しています。このプロジェクトは、誰もが参加できるものを作成することを目指しています

PHP 文字列操作: スペースを効果的に削除する実用的な方法 PHP 開発では、文字列からスペースを削除する必要がある状況によく遭遇します。スペースを削除すると文字列がきれいになり、その後のデータ処理と表示が容易になります。この記事では、スペースを削除するための効果的かつ実践的な方法をいくつか紹介し、具体的なコード例を添付します。方法1: PHP組み込み関数trim()を使用する PHP組み込み関数trim()を使用すると、文字列の両端のスペース(スペース、タブ、改行などを含む)を削除でき、非常に便利で簡単です。使用します。
