簡答題
一、談談你是如何理解JS異步編程的,EcentLoop、消息隊列都是做什么的,什么是宏任務,什么是微任務?
- JS 異步編程:
- 解答:
? ? ? ?JavaScript語言的執行環境是單線程,單線程是指一次只能完成一個任務,如果有多個任務,則需要排隊,等待前一個任務完成后,才能開始后一個任務。基于這種原因而產生了兩種執行任務的模式:同步模式和異步模式,且隨著JavaScript面臨的需求要來越多,它可以運行在瀏覽器、服務器上等,為了滿足這些需求,使得JavaScript的規模和復雜性也在持續增長,所以JavaScriptd中的異步編程也在不斷地調整,往更友好的方向發展,JavaScript異步編程經歷了回調函數、Promise、生成器函數Generator、以及現在Async/Await等幾個發展階段。
? ? ? ?在第一階段使用的是回調函數,它是最基本的異步操作方式,以Ajax請求最為常見,它是最簡單、容易理解和實現的異步編程;但是不利于代碼的閱讀和維護,各部分之間高度耦合,使得程序結構混亂,流程難以追蹤,每個任務只能指定一個回調函數,不能使用try catch捕獲錯誤,不能直接return,且極易出現回調地獄導致的調式困難,以及控制反轉導致的一系列信任問題
- 解答:
ajax(urlA, () => {
// 處理邏輯
ajax(urlB, () => {
// 處理邏輯
ajax(urlC, () => {
// 處理邏輯
})
})
})
? ? ? ?在第二階段引入了Promise,它是ES6推出的一種異步編程的解決方案。Promise是承諾的意思,這個承諾在未來某個時刻會有一個確定的答復,該承諾有三種狀態:等待中(pending)、完成(resolved)、拒絕(rejected)。Promise是個構造函數,接受一個函數作為參數。作為參數的函數有兩個參數:resolve和reject,分別對應完成和拒絕兩種狀態。我們可以選擇在不同時候執行resolve或reject去觸發下一個動作,執行then方法里的函數。Promise實現了鏈式調用,每次調用then之后返回的都是一個Promise對象,如果在then使用了return,return返回的值會被Promise.resolve()包裝。它很好的解決了回調函數中的回調地獄,解決了控制反轉導致的信任問題,將代碼的執行主動權拿了回來;但是Promise一旦狀態從等待改變為其他狀態就不再可變了,還有如果不設置回調函數,Promise內部拋出的錯誤。
new Promise((resolve, reject) => {
console.log('new Promise');
resolve('success'); })
console.log('end');
? ? ? ?第三階段使用生成器函數Generator,它是一種特殊的函數,其最大的特點是控制函數的執行。可以讓我們用同步的方式寫代碼,可以分步執行并得到異步操作的結果,能夠知曉異步操作的過程,以及切入修改異步操作的過程。但是需要手動去控制next(...),將回調成功的返回數據送回JavaScript的主流程中;
function *foo(x) {
let y = 2 * (yield (x + 1));
let z = yield (y / 3);
return (x + y + z);
}
let it = foo(5);
console.log(it.next()); // => {value: 6, done: false}
console.log(it.next(12)); // => {value: 8, done: false}
console.log(it.next(13));// => {value: 42, done: true}
? ? ? ?最新階段的Async/Await異步處理編程,其中async函數返回一個 Promise 對象,就是將函數返回使用Promise.resolve(),和then處理返回值一樣,可以使用then方法添加回調函數。await后邊一般跟Promise對象,async函數執行遇到await后,等待后面的Promise對象的狀態從pending變成resolve后,將resolve的參數返回并自動往下執行直到下一個await或結束。它解決了Generator需要手動控制next(...)執行的問題。但它存在一個缺陷是如果多個異步代碼沒有依賴性卻使用了await會導致性能降低。
async function test() {
console.log('1')
}
console.log(test) // Promise {<resolve>: "1"}
? ? ? ?整個異步過程都是通過內部的消息隊列和事件循環實現的
? ? ? ?每個階段的突破都是為了解決現有階段的技術問題,異步編程的發展也是一個循序漸進的過程。
- EventLoop、消息隊列:
- 解答:
? ? ? ?事件循環機制和消息隊列的維護是由事件觸發線程控制的,事件觸發線程是由瀏覽器渲染引擎提供的,它會維護一個消息隊列
? ? ? ?EventLoop是事件循環,主要負責監聽調用棧(執行棧)和消息隊列(任務隊列),一旦調用棧中所有的任務都結束了,事件循環就會從消息隊列中取出第一個回調函數,然后壓入到調用棧中,一旦消息隊列中發生了變化,事件循環就會監聽到
? ? ? ?消息隊列:消息隊列是類似隊列的數據結構,遵循先入先出(FIFO)的規則。如果把調用棧理解為正在執行的工作表,那么消息隊列則可以理解成待辦的工作表,JS引擎會先做完調用棧中所有的任務,然后通過事件循環從消息隊列中再取一個任務出來繼續執行,以此類推,整個過程隨時可以往消息隊列中再去放任務,這些任務在消息隊列中會排隊等待事件循環
? ? ? ?事件循環機制:
? ? ? ? ? ?1.JS引擎線程會維護一個執行棧,同步代碼會依次加入執行棧中,然后依次執行并出棧
? ? ? ? ? ?2.JS引擎線程遇到異步函數,會將異步函數交給相應的WebApi,并繼續執行后面的任務
? ? ? ? ? ?3.WebApi會在條件滿足的時候,將異步對應的回調加入到消息隊列中,等待執行
? ? ? ? ? ?4.執行棧為空時,JS引擎線程會去取消息隊列中的回調函數(如果有的話),并加入到執行棧中執行
? ? ? ? ? ?5.完成后出棧,繼續執行4的操作,直至消息隊列中的回調函數為空,以上便是事件循環的機制
- 解答:
- 宏任務、微任務
- 解答:
? ? ? ?在JS中,有兩類任務隊列:宏任務隊列(macrotask)和微任務隊列(microtask),宏任務可以由多個,微任務只有一個- macrotask:主代碼塊、setTimeout、setInterval、setImmediate、I/O、UI rendering等(可以看到,事件隊列中的每一個事件都是一個 macrotask,現在稱之為宏任務隊列)
- microtask:Promise、process.nextTick、Object.observer等
- 解答:
? ? ? ?每次執行棧執行的代碼即是一個宏任務,包括任務隊列(宏任務隊列)中的,因為執行棧中的宏任務執行完后會去取任務隊列(宏任務隊列)中的任務加入執行棧中
? ? ? ?在執行宏任務時遇到Promise等,會創建微任務(.then()里面的回調),并加入到微任務隊列隊尾;微任務必然是在某個宏任務執行的時候創建的,而在下一個宏任務開始之前,瀏覽器會對頁面重新渲染。同時,在上一個宏任務執行完成后,渲染頁面之前,會執行當前微任務隊列中的所有微任務。
? ? ? ?執行機制
? ? ? ? ? ?1.執行一個宏任務(執行棧中沒有就從消息隊列中獲取)
? ? ? ? ? ?2.執行過程中如果遇到微任務,就將微任務添加刀微任務的任務隊列中
? ? ? ? ? ?3.宏任務執行完畢后,立即執行當前微任務隊列中的所有微任務(依次執行)
? ? ? ? ? ?4.當前宏任務執行完畢,開始檢查渲染,然后GUI接管渲染
? ? ? ? ? ?5.喧渲染完畢后,JS引擎線程繼續,開始下一個宏任務(從宏任務隊列中獲取)
代碼題
一、將下面異步代碼使用Promise的方式改進
解答:使用Promise的方式改進實現如下:
// 使用Promise改進setTimeout異步代碼
new Promise((resolve, reject) => {
resolve('hello')
}).then(value => {
return value + ' ' + 'lagou' // value = hello
})
.then(value => {
return value + ' ' + 'I ? U' // value = hello lagou
})
.then(value => {
console.log(value) // hello lagou I ? U
})
二、基于以下代碼完成四個練習
練習1:使用組合函數fp.flowRight()重新實現下面這個函數
解答:fp.flowRight()重新實現如下:
//練習1
let isLastInstock = fp.flowRight(fp.prop('in_stock') , fp.last)
console.log(isLastInstock(cars)) //false 即最后一條數據的in_stock屬性值為fase
練習2:使用fp.flowRight(),fp.prop(),fp.first()獲取第一個car的name
解答:獲取第一個car的name實現如下:
// 練習2
let getFirstCarName = fp.flowRight(fp.props('name'), fp.first)
console.log(getFirstCarName(cars)) //[ 'Ferrari FF' ]
練習3:使用幫助函數_average()重構averageDollarValue,使用函數組合方式實現
解答:重構averageDollarValue實現如下:
let averageDollarValue = fp.flowRight(_average,fp.map(car => car.dollar_value))
console.log(averageDollarValue(cars)) //790700
練習4:使用flowRight寫一個sanitizeName()函數,返回一個下劃線連接的小寫字符串,把數組中的name轉換為這種形式,例如:sanitizeName(["Hello World"]) => ["hello_world"]
解答:使用flowRight寫一個sanitizeName()函數實現如下:
// 練習4
let _underscore = fp.replace(/\W+/g, '_')
//先遍歷數組把里面字符串轉成小寫,在遍歷數組將非單字字符轉換成下劃線
let sanitizeName1 = fp.flowRight( fp.map(_underscore), fp.map(fp.toLower))
//遍歷數組中使用fp.flowRight組合函數,先轉換成小寫,再將非單字字符轉換成下劃線
let sanitizeName2 = fp.flowRight( fp.map(fp.flowRight(_underscore, fp.toLower)))
console.log(sanitizeName2(["Hello World",'LaGou Study'])) //[ 'hello_world', 'lagou_study' ]
三、基于下面提供的四個代碼,完成后續的四個練習
練習1:使用fp.add(x,y)和fp.map(f,x)創建一個能讓functor里的值增加的函數ex1
解答:讓functor里的值增加的函數實現如下:
// 練習1
let ex1 = () => {
return fp.map(fp.add(1), maybe.map(x => x)._value)
}
console.log(ex1()) //[ 6, 7, 2 ]
練習2:實現一個函數ex2,能夠fp.first獲取列表的第一個元素
解答:獲取列表的第一個元素實現如下:
//練習2
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = () => {return fp.first(xs.map(x => x)._value)}
console.log(ex2()) //do
練習3:實現一個函數ex3,使用safeProp和fp.first找到user的名字的首字母
解答:使用safeProp和fp.first找到user的名字的首字母實現如下:
// 練習3
let safeProp = fp.curry(function (x, o){
return Maybe.of(o[x])
})
let user = {id: 2, name: 'Albert'}
let ex3 = () => {
return fp.first(safeProp(Object.keys(user).indexOf('name'),Object.values(user))._value)
}
console.log(ex3()) //A
練習4:使用MayBe重寫ex4,不要有if語句
// 練習4
let ex4 = function (n) {
if(n){
return(parseInt(n))
}
}
let ex5 = n =>{
return Maybe.of(n)._value
}
let ex6 = n =>{
return !!n? Maybe.of(n)._value : undefined
}
console.log(ex4(''))
console.log(ex5(''))
console.log(ex6(''))
注:ex5的寫法會在空字符串和null的時候導致輸出和ex4,ex6不一樣
四、手寫實現MyPromise源碼
要求:盡可能還原Promise中的每一個API,并通過注釋的方式描述思路和原理
MyPromise源碼(myPromise.js)
/*由于這個狀態頻繁使用,為了使用這個常量時編輯器有代碼提示并能夠復用,故把它定義為常量*/
const PENDING = 'pending' //等待
const FULFILLED = 'fulfilled' //成功
const REJECTED = 'rejected' //失敗
class MyPromise{
//構造函數 ==》 立即執行執行器 ==》 指的是傳遞過來的回調函數
constructor(executor) {
// 執行器錯誤處理 當執行器中代碼在執行過程中發生錯誤的時候,這個時候就讓promise狀態變成失敗
try{
executor(this.resolve, this.reject)
}catch (e) {
//捕獲執行器的錯誤
this.reject(e)
}
}
/*狀態是每個promise對象獨有的,故因該把狀態屬性定義為實例屬性*/
status = PENDING //promise狀態 ===》 默認值為等待
//由于每個promise對象都有自己成功之后的值,都有自己失敗的原因,故應該把這兩個屬性定義為實例的屬性
value = undefined //成功之后的值
reason = undefined //失敗后的原因
//由于then方法可能會被多次調用,聯想到數組能夠同時存儲多個函數 故在這里將屬性定義為數組
successCallback = [] //成功回調
failCallback = [] //失敗回調
//定義為箭頭函數的目的:將來在直接調用某個普通函數時,這個函數的this指向是window或者undefined,為了能讓this的指向為類的實例對象即promise對象,故而使用箭頭函數
resolve = value => {
//為了確保狀態確定后就不可更改,需加以判斷
//如果狀態不是等待,阻止程序向下執行
if(this.status !== PENDING) return
//將狀態更改為成功
this.status = FULFILLED
//將傳遞過來的值進行賦值 ==》 保存成功后的值
this.value = value
//判斷成功回調是否存在 如果存在 則調用并傳遞相應的參數
//數組中存儲了多個回調函數,我們需要循環遍歷這個數組,并在循環的過程中調用這個回調函數
//當數組的長度不為0時,就繼續執行循環體中的代碼
//考慮到需要從前往后執行,故調用數組的shift方法,把前面的回調函數彈出來
while(this.successCallback.length) this.successCallback.shift()()
}
reject = reason => {
//為了確保狀態確定后就不可更改,需加以判斷
//如果狀態不是等待,阻止程序向下執行
if(this.status !== PENDING) return
//將狀態更改為失敗
this.status = REJECTED
//將傳遞過來的值進行賦值 ==》 失敗后的原因
this.reason = reason
//判斷失敗回調是否存在 如果存在 則調用并傳遞相應的參數
//數組中存儲了多個回調函數,我們需要循環遍歷這個數組,并在循環的過程中調用這個回調函數
//當數組的長度不為0時,就繼續執行循環體中的代碼
//考慮到需要從前往后執行,故調用數組的shift方法,把前面的回調函數彈出來
while(this.failCallback.length) this.failCallback.shift()()
}
//then要被定義在原型對象中,并接受成功回調和失敗回調兩個參數
then (successCallback, failCallback){
//在調用then方法不傳遞任何參數的時候,要將最先的成功狀態依次傳遞給有回調函數的then方法
successCallback = successCallback? successCallback : value => value
failCallback = failCallback? failCallback : reason => {throw reason}
//then要實現鏈式調用,then方法必須返回promise對象,上一個回調的返回值傳遞給下一個then成功的回調
let promise2 = new MyPromise((resolve, reject)=>{
//判斷狀態并調用對應的回調函數并傳遞相應的參數
if(this.status === FULFILLED){
//由于promise2還沒被實例完,在實例過程中獲取不到,故使用異步實現,讓同步代碼先實現,再來執行異步代碼,這時候就能獲取到promise2
setTimeout(()=>{
//then方法中的回調函數在執行過程中報錯,這個錯誤要在下一個then的reject中捕獲到
try{
let x = successCallback(this.value)
resolvePromise( promise2, x, resolve, reject)
}catch (e) {
//捕獲then回調的錯誤
reject(e)
}
}, 0)
}else if(this.status ===REJECTED){
//由于promise2還沒被實例完,在實例過程中獲取不到,故使用異步實現,讓同步代碼先實現,再來執行異步代碼,這時候就能獲取到promise2
setTimeout(()=>{
// then方法中的回調函數在執行過程中報錯,這個錯誤要在下一個then的reject中捕獲到
try{
let x = failCallback(this.reason)
resolvePromise( promise2, x, resolve, reject)
}catch (e) {
//捕獲then回調的錯誤
reject(e)
}
}, 0)
}else{
//說明當前狀態等待,但不知是調用成功還是失敗的回調
//故將成功回調和是失敗回調存儲起來
this.successCallback.push(() => {
successCallback()
setTimeout(()=>{
try{
let x = successCallback(this.value)
resolvePromise( promise2, x, resolve, reject)
}catch (e) {
//捕獲then回調的錯誤
reject(e)
}
}, 0)
})
this.failCallback.push(() => {
setTimeout(()=>{
try{
let x = failCallback(this.reason)
resolvePromise( promise2, x, resolve, reject)
}catch (e) {
//捕獲then回調的錯誤
reject(e)
}
}, 0)
})
}
});
return promise2
}
//無論當前這個promise對象最終的狀態是成功還是失敗,finally中的這個回調函數始終都會執行一次e
//在finally方法的后面可以鏈式的調用then方法拿到當前這個promise對象返回的結果
finally (callBack){
//如何得到當前promise對象的狀態 ==》 this.then方法可以
return this.then(value=>{
//在finally中return了一個promise對象
return MyPromise.resolve(callBack()).then(()=>value)
},reason=>{
return MyPromise.resolve(callBack()).then(()=> {throw reason})
})
}
//用于當前promise對象失敗的情況,如果沒有傳遞失敗的回調函數,則會被catch捕獲到
catch(failCallback){
return this.then(undefined, failCallback)
}
// all方法是用來解決異步并發問題的,它允許我們按照異步代碼調用的順序得到異步執行的結果
// all的返回值也是一個promise對象,在后面可以鏈式對用then方法
// 在all中的promise對象都是成功的,那么all最后的結果也是成功,如果有一個失敗,那么結果就是失敗的
// 通過類調用,故為靜態方法,該方法接收一個數組作為參數
static all (array){
let results = []
let index = 0
return new MyPromise((resolve, reject) => {
function addData(key, value) {
results[key] = value
index++
// 針對異步情況下,判斷index的值和數組的長度相等,說明數組中所有的內容都執行完了,從而而決定是否調用resolve回調函數
if(index === array.length){
resolve(results)
}
}
//循環該數組
for(let i = 0; i < array.length; i++){
let current = array[i]
if(current instanceof MyPromise){
//promise對象,若成功的話將成功的值添加到數組中,若有一個失敗的話失敗的話則將失敗的原因通過reject回調
current.then(value => addData(i, value), reason => reject(reason))
}else{
//普通值 則直接添加到數組中
addData(i, array[i])
}
}
})
}
//如果是普通值,則創建一個promise對象,被將這個值包裹在promise對象中,然后把創建出來的promise對象作為resolve方法的返回值,才能在后面鏈式調用then方法
//如果是promise對象,則原封不動的將這個promise對象再作為resolve方法的返回值,所以才能在后面調用then方法,通過then方法的成功回調拿到這個promise對象的返回值
static resolve (value){
if(value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
}
//判斷 x 值時普通值還是promise對象
//如果是普通值,直接調用resolve
//如果是promise對象 查看promise對象返回的結果
//再根據promise對象返回的結果 決定調用resolve 還是調用reject
function resolvePromise(promise2, x, resolve, reject){
//相等表示自己返回自己(Promise對象自返回),這時應該調用reject回調函數
if(promise2 === x){
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if( x instanceof MyPromise){
//promise對象
x.then(resolve,reject)
}else{
//普通值
resolve(x)
}
}
//導出當前類
module.exports = MyPromise;
測試代碼(index.js)
/**
* 了解promise原理及原理代碼實現
* 1.Promise 就是一個類 在執行這個類的時候 需要傳遞一個執行器進去 執行器會立即執行 故放在promise類的構造器中
* 2.Promise 中有三種狀態 分別為 成功(fulfilled) 失敗(rejected) 等待(pending)
* pending -> fulfilled
* pending -> rejected
** 一旦狀態確定就不可更改
* 3.resolve和reject函數是用來更改狀態
* resolve:fulfilled
* reject:rejected
* 4.then方法內部做的事情就是判斷狀態 如果狀態成功 調用成功的回調函數 如果狀態失敗 調用失敗的回調函數 then被定義為原型對象當中
* 5.then成功回調有一個參數 表示成功之后的值 then失敗回調有一個參數 表示失敗的原因
**/
//導向MyPromise
const MyPromise = require('./myPromise')
//創建promise對象 resolve reject 兩個函數參數
let promise = new Promise((resolve, reject) => {
//把狀態變成成功 參數為成功后的值
resolve()
//把狀態編程失敗 參數為失敗的原因
reject()
})
//需實現promise的then方法,要先了解其中傳遞兩個回調函數及其含義
//調用then方法時,首先要去判斷promise狀態,成功的話則調用成功回調函數,失敗的話則調用失敗回調函數
//由于then能被任意一個promise對象調用,故應該將它定義在原型對象中
//then成功回調有一個參數(value) 表示成功之后的值 then失敗回調有一個參數(reason) 表示失敗的原因
promise.then(value=>{},reason=>{})
/** 1.類核心邏輯實現調用 **/
/*let promise1 = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
promise1.then(value => {
console.log(value) // 成功
}, reason => {
console.log(reason) // 失敗
})*/
/** 2.在Promise類中加入異步邏輯 **/
/*let promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
},2000)
// reject('失敗')
})
promise2.then(value => {
console.log(value) // 等待2s后輸出成功
}, reason => {
console.log(reason) // 等待2s后輸出失敗
})*/
/** 3.實現then方法多次調用添加多個處理函數 **/
/*let promise3 = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
promise3.then(value => {
console.log(1) // 1
console.log(value) // 成功
}, reason => {
console.log(reason) // 失敗
})
promise3.then(value => {
console.log(2) // 2
console.log(value) // 成功
}, reason => {
console.log(reason) // 失敗
})
promise3.then(value => {
console.log(3) // 3
console.log(value) // 成功
}, reason => {
console.log(reason) // 失敗
})*/
/** 4./5.實現then方法的鏈式調用 **/
//后面then方法拿到的值實際上是上一個回調函數的返回值
/*let promise4 = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
//測試4 then中返回值的情況
promise4.then(value => {
console.log(value) // 成功
return 100
}).then(value => {
console.log(value)
})
//測試5 then中返回promise情況
function other(){
return new MyPromise((resolve, reject) => {
resolve('other')
})
}
promise4.then(value => {
console.log(value) // 成功
return other()
}).then(value => {
console.log(value) // other
})*/
/** 6.then方法鏈式調用識別Promise對象自返回 **/
/*let promise6 = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
let p6 = promise6.then(value => {
console.log(value)
return p6
})
p6.then(value => {
console.log(value) //成功
},reason => {
console.log(reason.message) //Chaining cycle detected for promise #<Promise>
})*/
/** 7.捕獲錯誤及then鏈式調用其他狀態代碼補充 **/
// 執行器錯誤 then方法中的回調函數在執行過程中報錯,這個錯誤要在下一個then的reject中捕獲到
/*let promise7 = new MyPromise((resolve, reject) => {
// throw new Error('executor error')
// resolve('成功')
reject('失敗')
})
promise7.then(value => {
console.log(value) //成功
},reason => {
console.log(reason.message) //executor error
})
promise7.then(value => {
console.log(value) //成功
throw new Error('then error')
},reason => {
console.log(reason.message) //executor error
return 10000
}).then(value => {
console.log(value) // 成功 10000
// throw new Error('then error')
},reason => {
console.log('xxx') //xxx
console.log(reason.message) //then error
})*/
/** 8.then中方法編程可選參數 **/
/*let promise8 = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
promise8.then().then().then(value =>
console.log(value) //成功 失敗
)*/
function p1() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 2000)
})
}
function p2() {
return new MyPromise((resolve, reject) => {
// resolve('p2')
reject('失敗')
})
}
/** 9.promise.all方法的實現 **/
/*MyPromise.all(['a', 'b', p1(), p2(), 'c']).then(result =>
console.log(result) // [ 'a', 'b', 'p1', 'p2', 'c' ]
)*/
/** 10.promise.resolve方法的實現 **/
/*MyPromise.resolve(100).then(value => console.log(value))
MyPromise.resolve(p1()).then(value => console.log(value))*/
/** 10.promise.finally方法的實現 **/
/*p2().finally(()=>{
console.log('finally')
return p1()
}).then(value=>{
console.log(value) // p2
},reason=>{
console.log(reason)
})*/
/** 11.promise.catch方法的實現 **/
/*p2().then(value => console.log(value))
.catch(reason => console.log(reason))*/