then()
メソッドは Promise
を返します。最大2つの引数、 Promise
が成功した場合と失敗した場合のコールバック関数を取ります。
The source for this interactive example is stored in a GitHub repository. If you'd like to contribute to the interactive examples project, please clone https://github.com/mdn/interactive-examples and send us a pull request.
このデモのソースファイルは GitHub リポジトリに格納されています。デモプロジェクトに協力したい場合は、 https://github.com/mdn/interactive-examples をクローンしてプルリクエストを送信してください。
片方または両方の引数が省略されたり、関数ではないものが渡されたりした場合、 then
にはハンドラーが不足しますが、エラーは発生しません。 Promise
が状態 (fulfillment
(完了) または rejection
(拒否)) を受け入れるに当たって then
が呼び出された際に、 then
がハンドラーを持たない場合は、 then
が呼び出された元の Promise
の最後の状態を受け入れた、追加のハンドラーのない新しい Promise
が生成されます。
構文
p.then(onFulfilled[, onRejected]); p.then(value => { // fulfillment }, reason => { // rejection });
引数
返値
Promise
が完了するか拒否されると、それぞれのハンドラー関数 (onFulfilled
または onRejected
) が非同期に呼び出されます (現在のスレッドループにスケジュールされます)。ハンドラー関数のこの動作は特定の一連の規則に従います。もしハンドラー関数が・・・
- 値を返した場合、
then
によって返される Promise は返値をその値として解決します。 - 何も返さなかった場合、
then
によって返される Promise はundefined
の値で解決します。 - エラーを投げた場合、
then
によって返される Promise は、その値としてエラーを投げて拒絶されます。 - すでに解決している Promise を返した場合、
then
によって返される Promise は、その Promise の値をその値として返します。 - すでに拒絶された Promise を返した場合、
then
によって返される Promise は、その Promise の値をその値として拒絶されます。 - 他の pending 状態の Promise オブジェクトを返した場合、
then
によって返された Promise の解決/拒絶は、ハンドラーによって返された Promise の解決/拒絶結果に依存します。また、then
によって返された Promise の解決値は、ハンドラーによって返された Promise の解決値と同じになります。
以下は、 then
メソッドの非同期性を示す例です。
// using a resolved promise, the 'then' block will be triggered instantly,
// but its handlers will be triggered asynchronously as demonstrated by the console.logs
const resolvedProm = Promise.resolve(33);
let thenProm = resolvedProm.then(value => {
console.log("this gets called after the end of the main stack. the value received and returned is: " + value);
return value;
});
// instantly logging the value of thenProm
console.log(thenProm);
// using setTimeout we can postpone the execution of a function to the moment the stack is empty
setTimeout(() => {
console.log(thenProm);
});
// logs, in order:
// Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
// "this gets called after the end of the main stack. the value received and returned is: 33"
// Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 33}
説明
then
メソッドや Promise.prototype.catch()
メソッドは Promise
を返すので、チェーン可能です。 — これは composition と呼ばれる操作です。
例
then
メソッドの使用
var p1 = new Promise((resolve, reject) => {
resolve('Success!');
// or
// reject(new Error("Error!"));
});
p1.then(value => {
console.log(value); // Success!
}, reason => {
console.error(reason); // Error!
} );
チェーン
then
メソッドは Promise
を返すので、メソッドチェーンができます。
関数が then
にハンドラーとして渡されると Promise
を返します。同じ Promise
がメソッドチェーンの次の then
に現れます。次のスニペットは、非同期実行をシミュレートする、 setTimeout()
関数付きのコードです。
Promise.resolve('foo')
// 1. Receive "foo", concatenate "bar" to it, and resolve that to the next then
.then(function(string) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
string += 'bar';
resolve(string);
}, 1);
});
})
// 2. receive "foobar", register a callback function to work on that string
// and print it to the console, but not before returning the unworked on
// string to the next then
.then(function(string) {
setTimeout(function() {
string += 'baz';
console.log(string); // foobarbaz
}, 1)
return string;
})
// 3. print helpful messages about how the code in this section will be run
// before the string is actually processed by the mocked asynchronous code in the
// previous then block.
.then(function(string) {
console.log("Last Then: oops... didn't bother to instantiate and return " +
"a promise in the prior then so the sequence may be a bit " +
"surprising");
// Note that `string` will not have the 'baz' bit of it at this point. This
// is because we mocked that to happen asynchronously with a setTimeout function
console.log(string); // foobar
});
// logs, in order:
// Last Then: oops... didn't bother to instantiate and return a promise in the prior then so the sequence may be a bit surprising
// foobar
// foobarbaz
then()
の引数として渡された関数(ハンドラ)が値を返した場合は、 Promise.resolve (<ハンドラーが呼ばれて返された値>)
によって、返値を自動的に Promise
でラップします。
var p2 = new Promise(function(resolve, reject) {
resolve(1);
});
p2.then(function(value) {
console.log(value); // 1
return value + 1;
}).then(function(value) {
console.log(value + ' - A synchronous value works'); // 2 - A synchronous value works
});
p2.then(function(value) {
console.log(value); // 1
});
then
の引数として渡した関数が拒絶された Promise が返した場合や、例外 (エラー) が発生した場合は、拒絶された Promise を返します。
Promise.resolve()
.then(() => {
// Makes .then() return a rejected promise
throw new Error('Oh no!');
})
.then(() => {
console.log('Not called.');
}, error => {
console.error('onRejected function called: ' + error.message);
});
その他の場合はすべて、解決中の Promise が返されます。次の例では、チェーン上の以前の Promise が拒絶されていても、最初の then()
は解決中の Promise に含まれた 42
を返します。
Promise.reject()
.then(() => 99, () => 42) // onRejected returns 42 which is wrapped in a resolving Promise
.then(solution => console.log('Resolved with ' + solution)); // Resolved with 42
多くの場合、 catch
を使って失敗状態の Promise を補足する方が、 then
の 2 つのハンドラーを使って処理するよりも現実的です。下記の例を見てください。
Promise.resolve()
.then(() => {
// Makes .then() return a rejected promise
throw new Error('Oh no!');
})
.catch(error => {
console.error('onRejected function called: ' + error.message);
})
.then(() => {
console.log("I am always called even if the prior then's promise rejects");
});
Promise ベースの API を持った関数同士であれば、別の関数上に他の関数を実装することでチェーンを使うこともできます。
function fetch_current_data() {
// The fetch() API returns a Promise. This function
// exposes a similar API, except the fulfillment
// value of this function's Promise has had more
// work done on it.
return fetch('current-data.json').then(response => {
if (response.headers.get('content-type') != 'application/json') {
throw new TypeError();
}
var j = response.json();
// maybe do something with j
return j; // fulfillment value given to user of
// fetch_current_data().then()
});
}
onFulfilled
がプロミスを返した場合、 then
の返値はプロミスによって解決/拒否されます。
function resolveLater(resolve, reject) {
setTimeout(function() {
resolve(10);
}, 1000);
}
function rejectLater(resolve, reject) {
setTimeout(function() {
reject(new Error('Error'));
}, 1000);
}
var p1 = Promise.resolve('foo');
var p2 = p1.then(function() {
// Return promise here, that will be resolved to 10 after 1 second
return new Promise(resolveLater);
});
p2.then(function(v) {
console.log('resolved', v); // "resolved", 10
}, function(e) {
// not called
console.error('rejected', e);
});
var p3 = p1.then(function() {
// Return promise here, that will be rejected with 'Error' after 1 second
return new Promise(rejectLater);
});
p3.then(function(v) {
// not called
console.log('resolved', v);
}, function(e) {
console.error('rejected', e); // "rejected", 'Error'
});
window.setImmediate 形式のプロミスベースの代替処理
Function.prototype.bind()
を使用して、 Reflect.apply
(Reflect.apply()
) メソッドは (キャンセルできない) window.setImmediate
形式の関数を作成することができます。
const nextTick = (() => {
const noop = () => {}; // literally
const nextTickPromise = () => Promise.resolve().then(noop);
const rfab = Reflect.apply.bind; // (thisArg, fn, thisArg, [...args])
const nextTick = (fn, ...args) => (
fn !== undefined
? Promise.resolve(args).then(rfab(null, fn, null))
: nextTickPromise(),
undefined
);
nextTick.ntp = nextTickPromise;
return nextTick;
})();
仕様書
ブラウザーの互換性
BCD tables only load in the browser