Promise.prototype.then()

then() メソッドは Promise を返します。最大2つの引数、 Promise が成功した場合と失敗した場合のコールバック関数を取ります。

片方または両方の引数が省略されたり、関数ではないものが渡されたりした場合、 then にはハンドラーが不足しますが、エラーは発生しません。 Promise が状態 (fulfillment (完了) または rejection (拒否)) を受け入れるに当たって then が呼び出された際に、 then がハンドラーを持たない場合は、 then が呼び出された元の Promise の最後の状態を受け入れた、追加のハンドラーのない新しい Promise が生成されます。

構文

p.then(onFulfilled[, onRejected]);

p.then((value) => {
  // fulfillment
}, (reason) => {
  // rejection
});

引数

onFulfilled
Promise が成功したときに呼び出される Function です。この関数は1つの引数、 fulfillment value を持ちます。これが関数ではない場合は、内部的に "Identity" 関数 (受け取った引数を返す関数) に置き換えられます。
onRejected Optional
Promise が拒絶されたときに呼び出される Function です。この関数は1つの引数、 rejection reason を持ちます。これが関数ではない場合は、内部的に "Thrower" 関数 (引数として受け取ったエラーを投げる関数) に置き換えられます。

返値

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.log(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);
    }, 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);
  });

// 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');
});

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 );
  });

その他の場合はすべて、解決中 (resolving) の 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()
  });
}

前段の then() が Promise を返した場合、次段の 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.log('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.log('rejected', e); // "rejected", 'Error'
});

window.setImmediate 形式の Promise ベースの代替処理

Function.prototype.bind() を使用して、 Reflect.apply (Reflect.apply()) メソッドは (キャンセルできない) steImmediate 形式の関数を作成することができます。

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;
})();

仕様書

仕様書 状態 備考
ECMAScript 2015 (6th Edition, ECMA-262)
Promise.prototype.then の定義
標準 ECMA 標準としての初回定義
ECMAScript Latest Draft (ECMA-262)
Promise.prototype.then の定義
ドラフト

ブラウザーの互換性

Update compatibility data on GitHub
デスクトップモバイルサーバー
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung InternetNode.js
then()Chrome 完全対応 32Edge 完全対応 12Firefox 完全対応 29IE 未対応 なしOpera 完全対応 19Safari 完全対応 8WebView Android 完全対応 4.4.3Chrome Android 完全対応 32Firefox Android 完全対応 29Opera Android 完全対応 ありSafari iOS 完全対応 8Samsung Internet Android 完全対応 2.0nodejs 完全対応 0.12

凡例

完全対応  
完全対応
未対応  
未対応

関連情報