かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JavaScript (ES2015) 今更のPromise (3) 非同期処理を順番に実行するチョットマニアックなメモ

前回までのあらすじ

非同期処理を平行に実行する場合はPromise.allPromise.raceを使えばよいのですが、非同期処理で受け取った値を使って次の処理を行いたいなど順番(同期的)に実行したい場合のメモ

Promise~thenでのメソッドチェーン

たぶん一番正当なやり方。
Promiseオブジェクトを返す関数を実行してresolveされた時に実行されるthenメソッド内で、Promiseオブジェクトを返す関数をreturnすることで、Promise.prototype.thenのメソッドチェーンができる。
エラーになるとcatchに処理が流れる。

実行される関数

function myFunc(a, b = 0) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if(a) {
        let res = (a + b) * 2
        console.log(`(${a} + ${b}) * 2 = ${res}`)
        resolve( res )
      } else {
        reject(`Error a is ${a}`)
      }
    }, 10)
  })
}

function onReject(error) {
  console.log('>> onReject', error)
}

Promise.prototype.thenのメソッドチェーン

myFunc(1).then((res1) => {
  // res1 => 2
  return myFunc(2, res1)
})
.then((res2) => {
  // res2 => 8
  return myFunc(3, res2)
})
.then((res3) => {
  // res3 => 22
  console.log('> Promaise Complete', res3)
})
.catch(onReject)
// => (1 + 0) * 2 = 2
// => (2 + 2) * 2 = 8
// => (3 + 8) * 2 = 22
// => > Promaise Complete 22

エラーになる場合

myFunc(1).then((res1) => {
  // res1 => 2
  return myFunc(0, res1) // => reject になる
})
.then((res2) => {
  return myFunc(3, res2)
})
.then((res3) => {
  console.log('> Promaise Complete', res3)
})
.catch(onReject)
// => (1 + 0) * 2 = 2
// => >> onReject error a is 0

エラーが発生するとそれ以降のthenはスキップされる。
わかりやすいですね!

promise.then(successCallback, failureCallback) の場合

Promiseを使う - JavaScript | MDNに書かれているpromise.then(successCallback, failureCallback)で同期的に実行させようとする場合。

myFunc(1).then((res1) => {
  myFunc(2, res1).then((res2) => {
    myFunc(3, res2).then((res3) => {
      console.log('> Promaise Complete', res3)
    }, (error) => console.log('Error 3', error))
  }, (error) => console.log('Error 2', error))
}, (error) => console.log('Error 1', error))
// => (1 + 0) * 2 = 2
// => (2 + 2) * 2 = 8
// => (3 + 8) * 2 = 22
// => > Promaise Complete 22

// Errorになる場合
myFunc(1).then((res1) => {
  myFunc(0, res1).then((res2) => {
    myFunc(3, res2).then((res3) => {
      console.log('> Promaise Complete', res3)
    }, (error) => console.log('Error 3', error))
  }, (error) => console.log('Error 2', error))
}, (error) => console.log('Error 1', error))
// => (1 + 0) * 2 = 2
// => Error 2 Error a is 0

エラーの場合は先のthenをメソッドチェーンした場合と同じで、エラーになるとそれ以降の処理には流れず、エラーの時点で処理は完了になります。

thenの第一引数内で次のPromiseオブジェクトを返す関数を実行する必要があるので、同期処理が多くなるとコードのようにネストして見通しがあまり良くない感じです。ただ、エラーの処理をそれぞれ別に設定できるのはメリットかも知れません。

promise.then(successCallback, failureCallback) でthenのメソッドチェーンにした場合

ここからチョットマニアックかも。

myFunc(1)
.then(
  (res1) => myFunc(0, res1),
  (error) => console.log('Error 1', error)
)
.then(
  (res2) => myFunc(3, res2),
  (error) => console.log('Error 2', error)
)
.then(
  (res3) => console.log('> Promaise Complete', res3),
  (error) => console.log('Error 3', error)
)
.catch(onReject)
// => (1 + 0) * 2 = 2
// => Error 2 Error a is 0
// => > Promaise Complete undefined

上の例では、

  1. 1つめのthen: successCallback => muFunc(0, res1) (res1は2)
  2. 2つめのthen: failureCallback => console.log('Error 2', error)
  3. 3つめのthen: successCallback => console.log('> Promaise Complete', res3) (res3はundefined)

thenの第二引数にfailureCallbackが渡されていると、エラーの時にcatchに流れるのではなく、failureCallbackが呼ばれ、メソッドチェーンしてる次のthenに処理が流れるようです。

次のthenに流れた時に渡される引数について

先の例ではエラーになった時のコールバックではreturn console.log('Error 2')が実行されundefinedが返されているので、エラーで次のthenに渡される引数が「常にundefined」なのか「コールバック関数で返された値」がなのかが判断しきれません。

そこで、明示的に値を返して試してみます。

myFunc(0)
.then(
  (res1) => myFunc(2, res1),
  (error) => {
    console.log('Error 1')
    return 'Foo'
})
.then(
  (res2) => console.log('> Promise Complete:', res2),
  (error) => console.log('Error2', error)
)
// => Error 1
// => > Promise Complete: Foo

どうやら、エラーコールバックで返される(returnされる)値が次のthen内のsuccessCallbackに渡され実行されるようです。

Promise.reject()を返せば次のthenはエラーコールバックの方が呼ばれる

明示的にreturn Promise.reject()すると、次のthenの処理はエラーコールバックfailureCallbackの方に処理が流れました。

myFunc(0).then(
  (res1) => myFunc(2, res1),
  (error) => {
    console.log('Error 1')
    // Promise.reject を返す
    return Promise.reject('Reject 1')
})
.then(
  (res2) => console.log('> Promise Complete:', res2),
  (error) => console.log('Error2', error)
)
// => Error 1
// => Error2 Reject 1

 
promise.then(successCallback, failureCallback).then(...)の形式でメソッドチェーンをした場合は、

  • エラーの場合はcatchではなく、第二引数のfailureCallbackが実行される
  • 先の処理の成功失敗に関わらず、次のthenが呼ばれる
  • 次のthenのコールバックに渡される値は、先のコールバック内でreturnされた値
  • 次のthenでエラーに処理を流したい場合は、return Promise.reject("エラーコールバックに渡す引数")とする

という感じっぽいです。

先の非同期処理が成功しようが失敗しようが処理を待って次の処理を実行することができるので、値があれば値を使って、無ければ無いなりで必須な共通処理とか使い方はあるかもしれません。

myFunc(0)
.then(
  (res1) => {
    console.log('Success 1: res1 is ', res1)
    return myFunc(res1)
  },
  (error1) => {
    console.log('Error 1: default val 1')
    return 1
  }
)
.then(
  (res2) => {
    // 前のthenでPromise.rejectされないので、必ずココが実行される
    console.log('Common callback: res2 is', res2)
    return myFunc(res2)
  },
  (error2) => {
    console.log('Error 2:', error2)
    return Promise.reject('ERROR 2')
  }
)
.then(
  (res3) => {
    console.log('Promise complete:', res3)
  },
  (error) => {
    console.log('Error3', error)
  }
)
// => Error 1: default val 1
// => Common callback: res2 is 1
// => (1 + 0) * 2 = 2
// => Promise complete: 2

途中からthen~catchのメソッドチェーンにした場合

TASK

function myFunc2(a, b = 0) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if(!a) {
        reject(`Error a is ${a}`)
      }
      let res = (a + b) * 2
      console.log(`(${a} + ${b}) * 2 = ${res}`)
      resolve( res )
    }, 10)
  })
}

1. 成功した場合は、そのまま次のthenが実行される

myFunc2(1)
.then(
  (res1) => {
    console.log('Success 1: res1 is ', res1)
    return myFunc(2, res1)
  },
  (error1) => {
    console.log('Error 1')
    return 2
  }
)
.then(
  (res2) => {
    console.log('Success 2: res2 is ', res2)
  }
)
.catch(
  (error) => {
    console.log("Error catch", error)
  }
)
// => (1 + 0) * 2 = 2
// => Success 1: res1 is  2
// => (2 + 2) * 2 = 8
// => Success 2: res1 is  8

2. 失敗し、コールバック関数内で単に値をreturnした場合も、そのまま次のthenが実行される

myFunc2(0)
.then(
  (res1) => {
    console.log('Success 1: res1 is ', res1)
    return myFunc(2, res1)
  },
  (error1) => {
    console.log('Error 1')
    return 2
  }
)
.then(
  (res2) => {
    console.log('Success 2: res2 is ', res2)
  }
)
.catch(
  (error) => {
    console.log("Error catch", error)
  }
)
// => (0 + 0) * 2 = 0
// => Error 1
// => Success 2: res2 is  1 ... 先のエラー関数でreturnされた値が渡される

3. 失敗し、コールバック関数内でPromise.reject()を返した場合、catchに処理が流れる

myFunc2(0)
.then(
  (res1) => {
    console.log('Success 1: res1 is ', res1)
    return myFunc(2, res1)
  },
  (error1) => {
    console.log('Error 1')
    return Promise.reject("Reject!")
  }
)
.then(
  (res2) => {
    console.log('Success 2: res2 is ', res2)
  }
)
.catch(
  (error) => {
    console.log("Error catch", error)
  }
)
// => (0 + 0) * 2 = 0
// => Error 1
// => Error catch Reject! ... 先のコールバック関数で呼ばれたPromise.rejectに渡された値が引数で渡される

4. 成功したコールバック関数内でPromise.rejectが返された場合もcatchに処理が流れる

myFunc2(1)
.then(
  (res1) => {
    console.log('Success 1: res1 is ', res1)
    // 成功したけど、reject
    return Promise.reject(`res 1 is ${res1}`)
  },
  (error1) => {
    console.log('Error 1')
    return Promise.reject("Reject!")
  }
)
.then(
  (res2) => {
    console.log('Success 2: res2 is ', res2)
  }
)
.catch(
  (error) => {
    console.log("Error catch", error)
  }
)
// => (1 + 0) * 2 = 2
// => Success 1: res1 is  2
// => Error catch res 1 is 2

途中からthen~catchのメソッドチェーンを繋ぐことは問題ないようなので、successCallback, failureCallbackを使った場合の特性を知っていれば、特定のエラー処理だけ分けたいとかって場合などに使えるかもです。(あまりお行儀はよくなさそうだけど...)
 
さっさとawait /asyncに行けばいいのに色々と気になって試してしまいました。(学習が遅い言い訳


これから学ぶ JavaScript

これから学ぶ JavaScript

JavaScript (ES2015) 今更のPromise (2) Promiseが返された後の処理のメモ

前回のあらすじ

通常のPromiseを返す関数で明示的にresolve()reject()returnしてないと関数の終わりまで実行されるのでは?と思い調べてみました。

明示的に resolve/reject がreturnされていない場合

function myFunc(a, b = 0) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if(!a) {
        reject( `Error a is ${a}` )
      }
      let res = (a + b) * 2
      console.log(`(${a} + ${b}) * 2 = ${res}`)
      resolve( res )
    }, 10)
  })
}

myFunc(0)
.then((res1) => console.log(res1))
.then((res2) => console.log('CHAIN!!', res2))
.catch((error) => console.log(error))
// => (0 + 0) * 2 = 0
// => Error a is 0

やはりrejectが呼ばれた後も、関数の中は最後まで実行されていました。
恐らくresolve( res )も実行されているけど、元の呼び出し元では先にrejectが返っているので、再度thenに処理が流れたり、次のthenが呼び出されることもなく無視するようになってるっぽい。
ただresolve, rejectしてもその後の処理が無駄に実行されるので、Promiseオブジェクトを返す関数内ではそれ以上処理が必要ないならreturn resolve()return reject()とした方が良さそう。

並列処理 Promise.all, Promise.race は処理が返っても残りの処理は実行され続けている。

Promise.allでエラーが返ったり、Promise.raceで最初の値が返されても、呼び出していた処理は引き続き実行されているようです。
task

const task1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('> TASK-1')
      resolve('task1')
    }, 20)
  })
}

const task2 = () => {
  return new Promise((resolve, rejecy) => {
    setTimeout(() => {
      console.log('> TASK-2')
      resolve('task2')
    }, 5)
  })
}

const task3 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('> TASK-3')
      reject('REJECT TASK3')
    }, 10)
  })
}

Promise.all

Promise.all([task1(), task2(), task3()])
.then((res) => {
  console.log('>>> Promise.all', res)
})
.catch((error) => {
  console.log('>>> Promise.all ERROR')
})
// >> TASK-2
// >> TASK-3
// >>> Promise.all ERROR
// >> TASK-1

task3rejectされcatchに処理が流れているが、呼び出されたtask1はそのまま実行されている

Promise.race

Promise.race([task1(), task2(), task3()])
.then((res) => {
  console.log('>>> Promise.race', res)
})
.catch((error) => {
  console.log('>>> Promise.race ERROR')
})
// >> TASK-2
// >>> Promise.race task2
// >> TASK-3
// >> TASK-1

task2の処理が完了しても呼び出されていたtask1, task3は引き続き実行はされている

APIとかでエラーがあればエラーを返して処理をまるっと終了させたいような場合はprocess.exit(1)とかでプロセスを終了させてしまうのが良いのかな? (API作ったこと無いから正解かどうか判断しかねるけど)


JavaScript (ES2015) 今更のPromise

JavaScriptを扱うにあたって避けては通れない道、Ajaxとかタイマーとかの非同期処理…
ES7のawait/asyncを学ぶ方が良いのでしょうが、jQuery.deferred辺りで止まってしまっていると、いきなりawait/asyncは辛いので、Promiseをざっくり理解してawait/asyncに置き換える方法で学習するのが良いかと思い今更のPromise入門です。

Promiseの基本

new Promise(function(resolve, reject) {...})

  • resolve ... 処理が成功したら呼び出す関数
  • reject ... 処理が失敗したら呼び出す関数

 

function hi(name) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if(name) {
        resolve(`Hi, ${name}!`)
      } else {
        reject('ERROR name is empty!')
      }
    }, 100)
  })
}

.then()

Promise.prototype.then(successCallback, failureCallback)

返されるPromiseオブジェクトに.thenをメソッドチェーンさせることで、Promiseからresolve / rejectが呼び出されるまで待機して値を受け取れる

  • successCallback ... Promise.resolveが実行された時に呼び出される関数 (成功時に呼ばれる関数)
  • failureCallback ... Promise.rejectが実行された時に呼び出される関数 (失敗時に呼ばれる関数)
function onSuccess(res) {
  console.log('onSuccess', res)
}
function onReject(error) {
  console.log('onReject', error)
}

hi('星宮いちご').then(onSuccess, onReject)
// => onSuccess Hi, 星宮いちご!
hi().then(onSuccess, onReject)
// => onReject ERROR name is empty!

※ 非同期処理を同期的に実行したいときネストが深くなってしまう

hi('星宮いちご').then((res1) => {
  console.log('onSuccess 1', res1)
  hi('霧矢あおい').then((res2) => {
    console.log('onSuccess 2', res2)
    hi('紫吹蘭').then((res3)=> {
      console.log('onSuccess 3', res3)
    }, onReject)
  }, onReject)
}, onReject)
// => onSuccess 1 Hi, 星宮いちご!
// => onSuccess 2 Hi, 霧矢あおい!
// => onSuccess 3 Hi, 紫吹蘭!

thenのコールバック内でPromiseを返す関数をreturnすれば、Promise.thenをメソッドチェーンにすることができる

hi('星宮いちご').then((res1) => {
  console.log('onSuccess 1', res1)
  return hi('霧矢あおい')
})
.then((res2) => {
  console.log('onSuccess 2', res2)
  return hi('紫吹蘭')
})
.then((res3) => {
  console.log('onSuccess 3', res3)
})
// => onSuccess 1 Hi, 星宮いちご!
// => onSuccess 2 Hi, 霧矢あおい!
// => onSuccess 3 Hi, 紫吹蘭!

※ ただしメソッドチェーンしたthen内のコールバックはスコープが異なるので、前のthenのコールバックで受け取った値を直接参照することはできない

hi('星宮いちご').then((res1) => {
  return hi('霧矢あおい')
})
.then((res2) => {
  coneolse.log(res1)
  // => ReferenceError: res1 is not defined
})

※ エラーになった場合の処理が記述されてない場合、Promise.reject()が発生するとワーニングになってしまう。

hi()
.then((res1) => {
  console.log('res1')
  return hi()
})
.then((res2) => {
  console.log('Promise Complete')
})
// => UnhandledPromiseRejectionWarning: Unhandled promise rejection
// => DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

.catch()

Promise.prptpyupe.catch(onRejected)

Promiseが失敗した時に実行される節
エラーになった箇所以降のthenは実行されない

hi('神前美月').then((res1) => {
  console.log('onSuccess 1', res1)
  return hi() // Promise.rejectになる
})
.then((res2) => {
  console.log('onSuccess 2', res2)
  return hi('藤堂ユリカ')
})
.then((res3) => {
  console.log('onSuccess 3', res3)
})
.catch((error) => {
  // 共通のエラー処理
  console.log('Catch Error:', error)
})
// => onSuccess 1 Hi, 神前美月!
// => Catch Error: ERROR name is empty!

非同期処理を同期的に実行する時、特別なことがなければエラーはcatchで取るのが良さそう。

Promise.all

Promise.all() ... 引数で渡された全ての処理が完了したら結果の配列が返される。実行する処理に1つでもエラーがあるとエラーになる。

Promise.all([
  hi('神前美月'),
  hi('藤堂ユリカ'),
  hi('一ノ瀬かえで')
])
.then((res) => {
  // すべての非同期処理が完了したら、結果がArrayで返される
  console.log(res)
})
.catch((error) => {
  console.log(error)
})
// => [ 'Hi, 神前美月!', 'Hi, 藤堂ユリカ!', 'Hi, 一ノ瀬かえで!' ]

1つでもエラーがあると、最初にエラーになったものが返る

const task1 = new Promise((resolve, reject) => setTimeout(resolve, 50, 'TASK1'))
const task2 = new Promise((resolve, reject) => setTimeout(reject, 200, 'TASK2'))
const task3 = new Promise((resolve, reject) => setTimeout(reject, 100, 'TASK3'))

Promise.all([
  task1,
  task2,
  task3
])
.then((res) => console.log(res))
.catch((error) => console.log(error))
// => TASK3

Promise.race

Promise.race() ... 引数で渡された処理のいずれか1つが完了したら、完了した処理の値が返される。完了した最初の処理がエラーならエラー処理に流れる。※ 成功するものがあるか待つわけではない。

const task1 = new Promise((resolve, reject) => setTimeout(resolve, 100, '音城セイラ'))
const task2 = new Promise((resolve, reject) => setTimeout(resolve, 50, '冴草きい'))
const task3 = new Promise((resolve, reject) => setTimeout(resolve, 10, '風沢そら'))

Promise.race([
  task1,
  task2,
  task3,
])
.then((res) => {
 // 最初に完了した処理の返り値がそのまま渡される
 console.log(res)
})
.catch((error) => {
  console.log(error)
})
// => 風沢そら

最初に返されるものがエラー(reject)ならcatchに、並列処理中にエラーになるものがあっても、最初に返るのが正常な値(resolve)ならthenに処理が流れる

const task1 = new Promise((resolve, reject) => setTimeout(resolve, 100, '大空あかり'))
const task2 = new Promise((resolve, reject) => setTimeout(resolve, 50, '氷上すみれ'))
const task3 = new Promise((resolve, reject) => setTimeout(resolve, 10, '新条ひなき'))
const taskError = new Promise((resolve, reject) => setTimeout(reject, 30, 'ジョニー別府'))

Promise.race([
  task1,
  task2,
  taskError
])
.then((res) => console.log(res))
.catch((error) => console.log(`Error: ${error}`))
// => Error: ジョニー別府

Promise.race([
  task1,
  task2,
  taskError
  task3,
])
.then((res) => console.log(res))
.catch((error) => console.log(`Error: ${error}`))
// => 新条ひなき

Promise.resolve() と Promise.reject()

非同期処理がない場合など、わざわざPromiseオブジェクトを返す必要がない関数の場合、関数内でPromise.resolve()Promise.reject()を返すとと呼び出し元のPromiseに返すことができます。
returnしないと、関数が終了したundefinedが返される

function hi(name) {
  if( name.indexOf('ジョニー') === -1 ) {
    return Promise.resolve(`Hi, ${name}!`)
  } else {
    return Promise.reject(`ERROR: You are ${name}!`)
  }
}

Promise.all([
  hi('紅林珠璃'),
  hi('天羽まどか'),
  hi('黒沢凛')
])
.then((res) => console.log(res))
.catch((error) => console.log(error))
//=> [ 'Hi, 紅林珠璃!', 'Hi, 天羽まどか!', 'Hi, 黒沢凛!' ]

Promise.all([
  hi('ヤミ'),
  hi('ミヤ'),
  hi('ジョニー別府'),
])
.then((res) => console.log(res))
.catch((error) => console.log(error))
//=> ERROR: You are ジョニー別府!

 
Promiseの基本的な使い方をまとめてみました。
実際の使われた方とかアンチパターンとかは、まだ解ってないですが... (その辺りはawait/asyncに移行しちゃってから考えたほうが良い気がします)


[参考]

Mac Apache2.4が動かなくなった

昔作ったサイトのメンテで久々にhomebrewで入れたローカル環境のApacheを起動しようとしたら次のようなエラーが出て起動できなくなっていました。

httpd: Syntax error on line 170 of /usr/local/etc/apache2/2.4/httpd.conf:
Cannot load /usr/local/opt/php56/libexec/apache2/libphp5.so into server: dlopen(/usr/local/opt/php56/libexec/apache2/libphp5.so, 10):
Library not loaded: /usr/local/lib/libjpeg.8.dylib\n
Referenced from: /usr/local/opt/php56/libexec/apache2/libphp5.so\n
Reason: image not found

libjpeg.8.dylibが無いのが原因っぽいです。
/user/local/Cellar/jpegを見に行くと9clibjpeg.9.dylibにバージョンアップされていました。(brew updateや何かのパッケージのアップデートの際に消えたのでしょうか...)

libjpeg を前のバージョンに切り替える

brew cleanupをしていなければ、古いバージョンのファイルが残っているので、次のコマンドで古いバージョンに切り替えられるようです。

$ brew switch libjpeg 8d

brew cleanupをすでにしてしまっていた場合

私は既にbrew cleanupしてしまっていたので、上記では切り替えることができませんでした。
php72 install libjpeg.8.dylib not found · Issue #4358 · Homebrew/homebrew-php · GitHub
こちらのコメントを参考に手動でlibjpeg.8.dylibをインストールしました。

$ wget -c http://www.ijg.org/files/jpegsrc.v8d.tar.gz
$ tar xzf jpegsrc.v8d.tar.gz
$ cd jpeg-8d
$ ./configure
$ make
$ cp ./.libs/libjpeg.8.dylib /usr/local/opt/jpeg/lib

このままでは、まだlibjpeg.8.dylibが読み込めないようだったので、/usr/local/lib/内にlibjpeg.8.dylibシンボリックリンクを作成しました。

$ cd /usr/local/lib/
$ ln -s ../opt/jpeg/lib/libjpeg.8.dylib libjpeg.8.dylib

これで無事Apacheが動く状態に戻せました。
ただ、/usr/local/opt/jpeg/lib/usr/local/Cellar/jpeg/9c/lib/シンボリックリンクなようでパスを見る感じだとインストールしたlibjpeg.8.dylib9cという別バージョンのフォルダに入ってしまっています。少し気持ち悪いですし、またlibjpegがアップデートされると問題が発生しそうなので、恒久的解決にはなって無さそうです...


[参考]

[未解決っぽい] Mac/Chrome カスペルスキーアップデートしたらTwitterの画像やビデオ・CSSも読み込まなくなった件

Macですが、いちおセキュリティソフトとしてカスペルスキーを入れていました。
カスペルスキーにアップデートで再起動しろと言われたので再起動したところ、Chromeで安息の地Twitterの画像やビデオ、だけでなくCSSまで読み込まなくなっていました。

読み込めてないエラーはこんな感じ

GET *.twimg.com <画像とかのURL>
net::ERR_SSL_VERSION_INTERFERENCE

どうやら別ドメインにあるコンテンツの取得が弾かれているようです。
カスペルスキーを終了すると問題なくTwitterが表示されたので、カスペルスキーのアップデートが原因っぽい…

  • Kaspersky Internet Security v 19.0.0.294a.b

心の安らぎTwitterを見れなくするとか許さん!
とはいえ、セキュリティ機能をOFFにしてしまうと本末転倒です。(ライセンス購入してるし...)

カスペルスキーにバグ報告をしたところ、「プロテクションのネット決済保護 ~ (HTTPS)」機能が原因で、現在調査中なので暫くその機能をOFFにしてくださいとの事でした。追記 2018 11/11

ウェブ保護・危険サイト診断が原因っぽい。

見れない状態のTwitterで開発者ツールをみると下記URL関係の読み込みがエラーになっていました。

  • CSS関連の //abs.twimg.com
  • 投稿された画像関連の //pbs.twimg.com
  • Gif動画などビデオ関連の //video.twimg.com

上記URLが読み込めるようになれば約束の地を取り戻せそうです。

1. カスペルスキーの脅威の除外リストにTwitter関係のURLを入れる

  • カスペルスキーのメニューから「環境設定 > 脅威」を選択
    f:id:kikiki-kiki:20181102071955p:plain -「除外するリストの編集...」を選択
  • 「信頼するWebサイトのアドレス」にTwitter関連のURLを入れる (とりあえず片っ端から入れたので無駄があるかも)
    f:id:kikiki-kiki:20181102082351p:plain

追加したリストは下記の通りです
twimg.comの読み込みが許可されれば、問題無さそうなのですが一応サブドメインも追加しておきました。

https://twitter.com
https://twimg.com
https://pbs.twimg.com
https://abs.twimg.com
https://video.twimg.com

この状態で、Twitterをリロードしても画像など表示されないままでした…

2. Chrome拡張が原因?

f:id:kikiki-kiki:20181102071926p:plain
Chrome拡張プラグインの「ウェブ保護」「危険サイト診断」をオフににすると、Twitterの画像を読み込めるようになりました。ただ、せっかくあるセキュリティ機能をOFFにしたままだと、意味がないのでこの機能をONにできる方法を探ってみました。

3. ウェブの保護の有効化をChromeの拡張から行うとダメだった

色々再起動やON/OFFを繰り返してみた結果、どうやら「ウェブの保護」をChrome拡張からONにするとダメなようでした。(アプリ側の除外設定が読み込まれない?)
「ウェブの保護」をONにする為に下記手順を行いました。

  1. 再度Chrome拡張の「ウェブ保護」・「危険サイト診断」をOFFにします
  2. Twitterをリロードして、画像などが表示されていることを確認
  3. Chromeカスペルスキーを念の為、再起動
  4. (Twitterをリロードして、画像などが表示されていることを確認
  5. Chrome拡張の「危険サイト診断」をONにする
  6. (Twitterをリロードして、画像などが表示されていることを確認
  7. カスペルスキーのアプリからウェブ保護を「有効にする」
    f:id:kikiki-kiki:20181102072036p:plain
    これでChrome拡張の「ウェブ保護」がONになります
  8. 最後にTwitterをリロードして、画像などが表示されていればOK

4. プロテクションのHTTPSの設定のせい?

上記で一旦 画像などが読み込めるようになったのですが、ページ遷移したりすると、また読み込めないものが出てくるという状態になりました。
読み込めてないエラーは以前と同じくnet::ERR_SSL_VERSION_INTERFERENCE
SSL/HTTPS関係っぽい...

カスペルスキー > 環境設定 > プロテクション設定 の全般から 「□ ネット決済保護 ~ (HTTPS)」のチェックを外すと上記のエラーは発生しなくなりました。
f:id:kikiki-kiki:20181102091103p:plain

その後、再び項目にチェックをしてTwitterをリロードしたら画像が読み込めていました... (なるほど、よくわからん!  

各手順ごとに念の為Twitterを再起動して問題がないか確認しています。
表示されなくなるステップがあれば、その箇所あたりに原因があるハズ。

リストにURLを追加した後は、下記手順で

  1. カスペルスキーのプロテクション設定から「HTTP」のチェックを外す
  2. Chrome拡張から「WEBの保護をOFF」
  3. カスペルスキー
  4. カスペルスキーを再起動
  5. カスペルスキーのプロテクション「WEBの保護を有効にする」
  6. カスペルスキーのプロテクション「HTTPS」に再チェック
    ※ 再チェックをして読み込めなくなったらチェックを外すと読み込めるっぽい。

 
脅威のスキャンの除外リストに入れているのでTwitterそのものに危険が入っちゃうと危険が危ないのですが、とりあえずコレで安息のインターネッツライフを取り戻せる状態になりました。色々試してみながら、たまたま上手く行っただけなので上記手順でうまくいかない場合はリストの見直しや、拡張のON/OFF・カスペルスキーの再起動など色々順番を変えて試してみるとうまくいくかもです。(確証ないですが...)

しかし、Chromeを再起動するとモーダルで画像は表示されるのに、タイムライン上だと画像が白枠になって表示されない状態になるものがあったり、ページ遷移で読み込めなくなってしまったりします。
とりあえず「プロテクションのHTTPS」のチェックを外すとリロードで読み込める場合が多いのですが、セキュリティ上はあまりよろしくない状態な気もします。

この問題はカスペルスキーがブロックしてる問題と、ブラウザChromeが読み込んだページをキャッシングしてしまうために、どれがクリティカルな問題になっているのかが大変わかりづらくなっているように思います...


安住の地 1 (BIC COMICS IKKI)

安住の地 1 (BIC COMICS IKKI)