构造对象 new new 运算符接受一个函数 F 及其参数: new F(arguments…).这个一过程分为:
创建类的实例.这步是把一个空的对象的 proto 属性设置为 F.prototype. 
初始化实例.函数 F 被传入参数并调用.关键字 this 被设定为该实例. 
返回实例. 
 
对于实例对象来说,都是通过 new 产生的,无论是function Foo()还是let a = { b : 1 } 。
1 2 3 4 5 6 7 8 9 function  person (name, age ) {  this .name  = name;   this .age  = age;   this .sayName  = function  ( ) {     console .log (this .name );   }; } p1 = new  person ("tom" , 12 ); 
 
instanceof instanceof 是一个操作符,可以判断对象 是否为某个类型的实例
1 2 3 p1 instanceof  person;  p1 instanceof  Object ;  1  instanceof  Number ; 
 
原型与原型链 
当new一个函数的时候会创建一个对象,函数.prototype等于被创建对象.__proto__ 
一切函数都是由 Function 这个函数创建的,所以Function.prototye === 被创建函数.__proto__ 
一切函数的原型对象都是由Object这个函数创建的,所以Object.prototype === 一切函数.prototype.__proto__ 
 
__proto__把对象和原型连接起来,形成原型链
 
this 在函数被直接调用时this绑定到全局对象.即 window
1 2 3 4 5 console .log (this ); function  fn1 ( ) {  console .log (this );  } fn1 (); 
 
内部函数 函数嵌套产生的内部函数的this不是他的父函数,仍然是全局,window
1 2 3 4 5 6 7 8 function  fn0 ( ) {  function  fn ( ) {     console .log (this );   }   fn (); } fn0 ();
 
setTimeOut()和 setInterval() 这两个函数执行的函数 this 也是全局
1 2 3 4 5 6 7 8 9 10 document .addEventListener (  "click" ,   function  (e ) {     console .log (this );     setTimeout (function  ( ) {       console .log (this );     }, 200 );   },   false  ); 
 
DOM 对象绑定事件 在事件处理程序中 this 代表事件源 DOM 对象
1 2 3 4 5 6 7 8 9 10 11 12 document .addEventListener (  "click" ,   function  (e ) {     console .log (this );      var  _document = this ;     setTimeout (function  ( ) {       console .log (this );        console .log (_document);     }, 200 );   },   false  ); 
 
Function.prototype.bind() bind 改变 this 的指向.返回一个新函数,并使函数内部的 this 为传入的第一个参数,并不执行 
1 2 var  fn3 = obj1.fn .bind (obj1);fn3 ();
 
使用 call 和 apply 设置 this call,apply 调用一个函数,传入函数执行上下文及其参数,并立即执行 
1 2 fn.call (context,param1,param2...) fn.apply (context,paramArray) 
 
第一个参数都是希望设置的 this 对象
举例
1 2 3 4 5 6 7 8 function  sum ( ) {     Array .prototype  .forEach .call (arguments , function  (value ) {     console .log (value);   }); } sum (3 , 4 , 1 , 6 ); 
 
1 2 3 4 5 6 7 8 9 10 function  sum ( ) {  var  result = 0 ;      Array .prototype  .forEach .call (arguments , function  (value ) {     console .log (value);     return  (result += value);   });   console .log (result); } sum (3 , 4 , 1 , 6 ); 
 
arguments 
在函数调用时,会自动在该函数内部生成一个名为 arguments 的隐藏对象 
该对象类似于数组,可以使用[]运算符获取函数调用时传递的实参 
只有函数被调用时,arguments 对象才会创建,未调用时其值为 null 
 
1 2 3 4 5 6 7 8 function  fn5 (name, age ) {  console .log (arguments );   name = "XXX" ;   console .log (arguments );   arguments [1 ] = 30 ;   console .log (arguments ); } fn5 ("Byron" , 20 );
 
研究 this 1 2 3 4 5 6 7 8 9 var  obj = {  foo : function  ( ) {     console .log (this );   }, }; var  bar = obj.foo ;obj.foo ();  bar (); 
 
实际上的正常调用方式
func.call(context, p1, p2)
其他简化方式都可以转化
1 2 3 4 5 func (p1, p2) 等价于func.call (undefined , p1, p2) obj.child .method (p1, p2) 等价于 obj.child .method .call (obj.child , p1, p2) 
 
this,就是上面代码中的 context。就这么简单。
this 是你 call 一个函数时传的 context,由于你从来不用 call 形式的函数调用,所以你一直不知道。
浏览器规则:如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)
 
上面的代码就解释的通了
1 2 3 4 5 6 7 8 9 10 11 12 13 var  obj = {  foo : function  ( ) {     console .log (this );   }, }; var  bar = obj.foo ;obj.foo ();  bar ();
 
Event Handler 中的 this 1 2 3 btn.addEventListener ("click" , function  handler ( ) {   console .log (this );  }); 
 
当使用 addEventListener() 为一个元素注册事件的时候,句柄里的 this 值是该元素的引用。其与传递给句柄的 event 参数的 currentTarget 属性的值一样。
 
jQuery Event Handler 中的 this 那么下面代码中的 this 是什么呢:
1 2 3 $ul.on ("click" , "li" , function  ( ) {   console .log (this ); }); 
 
当 jQuery 的调用处理程序时,this 关键字指向的是当前正在执行事件的元素。对于直接事件而言,this 代表绑定事件的元素。对于代理事件而言,this 则代表了与 selector 相匹配的元素。(注意,如果事件是从后代元素冒泡上来的话,那么 this 就有可能不等于 event.target。)若要使用 jQuery 的相关方法,可以根据当前元素创建一个 jQuery 对象,即使用 $(this)。
 
[]语法 1 2 3 4 5 function  fn ( ) {  console .log (this ); } var  arr = [fn, fn2];arr[0 ]();  
 
我们可以把 arr0 想象为 arr.0( ),虽然后者的语法错了,但是形式与转换代码里的 obj.child.method(p1, p2) 对应上了,于是就可以愉快的转换了:
1 2 3 4          arr[0 ]() 假想为    arr.0 () 然后转换为 arr.0 .call (arr) 那么里面的 this  就是 arr 了 
 
call、apply 、函数执行的本质 当我们执行一个函数,以下几种调用方式等价
1 2 3 4 5 6 7 8 "use strict" ;function  fn (a, b ) {  console .log (this ); } fn (1 , 2 );fn.call (undefined , 1 , 2 ); fn.apply (undefined , [1 , 2 ]); 
 
在严格模式下, fn 里的 this 就是 call 的第一个参数,也就是 undefined。
在非严格模式下(不加”use strict”), call 传递的第一个参数如果是 undefined 或者 null, 那 this 会自动替换为 Window 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var  obj = {  fn : function  (a, b ) {     console .log (this );   },   child : {     fn2 : function  ( ) {       console .log (this );     },   }, }; obj.fn (1 , 2 ); obj.fn .call (obj, 1 , 2 );  obj.fn .apply (obj, [1 , 2 ]); obj.child .fn2 (); obj.child .fn2 .call (obj.chid );  
 
箭头函数中的 this 
箭头函数会捕获其所在上下文的 this 值作为自己的 this 值,自己本身并没有 this 值. 
箭头函数的 this 永远指向其上下文的 this,任何方法都改变不了其指向,如 call,bind,apply. 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let  app = {  fn1 : function  (a ) {     console .log (this );    },   fn2 (a ) {     console .log (this );    },   fn3 : (a ) =>  {     console .log (this );    }, }; app.fn2 .call (app); app.fn3 .call (它上下文的this ); 
 
箭头函数的复杂情况示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 var  app = {  fn1 ( ) {     setTimeout (function  ( ) {       console .log (this );     }, 10 );   },   fn2 ( ) {     setTimeout (() =>  {       console .log (this );     }, 20 );   },   fn3 ( ) {     setTimeout (       function  ( ) {         console .log (this );       }.bind (this ),       30      );   },   fn4 : () =>  {     setTimeout (() =>  {       console .log (this );     }, 40 );   }, }; app.fn1 (); app.fn2 (); app.fn3 (); app.fn4 (); 以上代码相当于; var  app = {  fn1 ( ) {     function  fn ( ) {       console .log (this );     }             },   fn2 ( ) {             },   fn3 ( ) {             },   fn4 : () =>  {                  }, }; 
 
函数的执行环境 JavaScript 中的函数既可以被当作普通函数执行,也可以作为对象的方法执行,这是导致 this 含义如此丰富的主要原因
一个函数被执行时,会创建一个执行环境(ExecutionContext),函数的所有的行为均发生在此执行环境中,构建该执行环境时,JavaScript 首先会创建 arguments 变量,其中包含调用函数时传入的参数
接下来创建作用域链,然后初始化变量。首先初始化函数的形参表,值为 arguments 变量中对应的值,如果 arguments 变量中没有对应值,则该形参初始化为 undefined。
如果该函数中含有内部函数,则初始化这些内部函数。如果没有,继续初始化该函数内定义的局部变量,需要注意的是此时这些变量初始化为 undefined,其赋值操作在执行环境(ExecutionContext)创建成功后,函数执行时才会执行,这点对于我们理解 JavaScript 中的变量作用域非常重要,最后为 this 变量赋值,会根据函数调用方式的不同,赋给 this 全局对象,当前对象等
至此函数的执行环境(ExecutionContext)创建成功,函数开始逐行执行,所需变量均从之前构建好的执行环境(ExecutionContext)中读取.
每个执行上下文中都有三个重要的属性
 
变量对象(VO),包含变量、函数声明和函数的形参,该属性只能在全局上下文中访问 
作用域链(JS 采用词法作用域,也就是说变量的作用域是在定义时就决定了) 
this 
 
三种变量 实例变量:(this)类的实例才能访问到的变量
静态变量:(属性)直接类型对象能访问到的变量
私有变量:(局部变量)当前作用域内有效的变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function  ClassA ( ) {  var  a = 1 ;    this .b  = 2 ;  } ClassA .c  = 3 ; console .log (a); console .log (ClassA .b ); console .log (ClassA .c ); var  classa = new  ClassA ();console .log (classa.a ); console .log (classa.b ); console .log (classa.c ); 
 
继承 继承是指一个对象直接使用另一个对象的属性和方法.
如果实现以下两点就实现了继承
得到一个类的属性 
得到一个类的方法 
 
原型继承,核心在于在子类的构造函数中通过parent.call(this)继承父亲的属性,然后改变子类的原型为new parent()来继承父类的函数.
 
属性的获取 对象属性的获取是通过构造函数的执行.在一个类中执行另外一个类的构造函数,就可以把属性赋值到自己内部,
但是需要把环境改到自己的作用域内,用call修改 this 的指向即可.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function  Person (name, sex ) {  this .name  = name;   this .sex  = sex; } Person .prototype  .printName  = function  ( ) {  console .log (this .name ); }; function  Male (age ) {  this .age  = age; } Male .prototype  .printAge  = function  ( ) {  console .log (this .age ); }; function  Male (name, sex, age ) {  Person .call (this , name, sex);   this .age  = age; } Male .prototype  .printAge  = function  ( ) {  console .log (this .age ); }; var  m = new  Male ("Tom" , "male" , 10 );console .log (m.sex ); 
 
继承的范例 1 2 3 4 5 6 7 8 9 10 function  Male (name, sex, age ) {  Person .call (this , name, sex);   this .age  = age; } Male .prototype   = Object .create (Person .prototype  );Male .prototype  .printAge  = function  ( ) {  console .log (this .age ); }; 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function  Person (name, sex ) {  this .name  = name;   this .age  = age; } Person .prototype  .printName  = function  ( ) {  console .log (this .name ); }; function  Male (name, sex, age ) {  Person .call (this , name, age);   this .sex  = sex; } Male .prototype   = new  Person ();Male .prototype  .constuctor  = Male ;Male .prototype  .printAge  = function  ( ) {  console .log (this .age ); }; var  man = new  Male ("Tom" , "Male" , 10 );man.printName ();  
 
hasOwnProperty 判断属性是自己的还是继承的
1 2 m.hasOwnProperty ("name" );  m.hasOwnProperty ("printName" );  
 
Promise 对象 回调地狱 下列代码实现按顺序执行,1 秒后执行 fn1,再过 1 秒执行 fn2,再过 1 秒执行 fn3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function  fn1 (callback ) {  setTimeout (() =>  {     console .log ("fn1" );     callback ();   }, 1000 ); } function  fn2 (callback ) {  setTimeout (() =>  {     console .log ("fn2" );     callback ();   }, 1000 ); } function  fn3 ( ) {  setTimeout (() =>  {     console .log ("fn3" );   }, 1000 ); } fn1 (function  ( ) {  fn2 (function  ( ) {     fn3 ();   }); }); 
 
由于层层嵌套,形成回调地狱 .(如果套个十几二十个,真的要崩溃)
什么是 Promise Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值.(MDN 解释)
Promise 是一个对象,对象里存储着状态.分别是 pending(等待态),fulfilled(完成态),rejected(拒绝态)
Promise 启动之后,当满足成功的条件时我们让状态从 pending 变成 fullfilled (执行 resolve);当满足失败的条件,我们让状态从 pending 变成 rejected(执行 reject)
面试官想听的版本 :
所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理,让开发者不用再关注于时序和底层的结果。Promise 的状态具有不受外界影响和不可逆两个特点。
then 函数会返回一个 Promise 实例,并且该返回值是一个新的实例而不是之前的实例。因为 Promise 规范规定除了 pending 状态,其他状态是不可以改变的,如果返回的是一个相同实例的话,多个 then 调用就失去意义了.
then 的参数有两个,也是一个成功函数,一个失败函数.
1 const  promise2 = doSomething ().then (successCallback, failureCallback);
 
Promise+ajax 范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 function  getIp ( ){  return  new  Promise (function (resolve,reject ){     var  xhr = XMLHttpRequest ()     xhr.open ('GET' ,'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getIp' ,true )     xhr.onload  = function ( ){       var  retJson = JSON .parse (xhr.responText )       resolve (retJson.ip )     }     xhr.onerror  = function ( ){       reject ('获取IP失败' )     }     xhr.send ()   }) } function  getCityFromIp (ip ){  return  new  Promise (function (resolve, reject ){     var  xhr = XMLHttpRequest ()     xhr.open ('GET' , 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip=' +ip, true )     xhr.onload  = function ( ){       var  retJson = JSON .parse (xhr.responText )       resolve (retJson.city )     }     xhr.onerror  = function ( ){       reject ('获取city失败' )     }     xhr.send ()   }) } function  getWeatherFromCity (city{   return  new  Promise (function (resolve, reject){     var  xhr = XMLHttpRequest()     xhr.open('GET' , 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getWeatherFromCity?city=' +city, true )     xhr.onload = function (){       var  retJson = JSON .parse(xhr.responText)       resolve(retJson)     }     xhr.onerror = function (){       reject('获取天气失败' )     }     xhr.send()   }) } )getIp ().then (function (ip ){  return  getCityFromIp (ip) }).then (function (city ){   return  getWeatherFromCity (city) }).then (function (data ){   console .log (data) }).catch (function (e ){   console .log ('出现了错误' ,e) }) 
 
Promise.all 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function  getCityFromIp (ip ){  return  new  Promise (function (resolve, reject ){     var  xhr = XMLHttpRequest ()     xhr.open ('GET' ,'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip=' +ip',true)      xhr.onload = function(){       var retJson = JSON.parse(xhr.responText)       resolve(retJson)     }     xhr.onerror = function(){       reject(' 获取city失败')     }     xhr.send()   }) } var p1 = getCityFromIp(' 10.10 .10 .1 ') var p2 = getCityFromIp(' 10.10 .10 .2 ') var p3 = getCityFromIp(' 10.10 .10 .3 ') //Promise.all, 当所有的 Promise 对象都完成后再执行 Promise.all([p1,p2,p3]).then(data => {   console.log(data) }) 
 
Promise.race 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function  getCityFromIp (ip ) {  var  promise = new  Promise (function  (resolve, reject ) {     var  xhr = new  XMLHttpRequest ();     xhr.open (       "GET" ,       "https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip="  +         ip,       true      );     xhr.onload  = function  ( ) {       var  retJson = JSON .parse (xhr.responseText );        resolve (retJson);     };     xhr.onerror  = function  ( ) {       reject ("获取city失败" );     };     setTimeout (() =>  {       xhr.send ();     }, Math .random () * 1000 );   });   return  promise; } var  p1 = getCityFromIp ("10.10.10.1" );var  p2 = getCityFromIp ("10.10.10.2" );var  p3 = getCityFromIp ("10.10.10.3" );Promise .race ([p1, p2, p3]).then ((data ) =>  {  console .log (data); }); 
 
callback&Promise&async/await 把一个需求不断简化
需求如下:
读取 a.md 文件,得到内容 
把内容转换成 HTML 字符串 
把 HTML 字符串写入 b.html 
 
callback()处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var  fs = require ("fs" );var  markdown = require ("markdown" ).markdown ;fs.readFile ("a.md" , "UTF-8" , function  (err, str ) {   if  (err) {     return  console .log (err);   }   var  html = markown.toHTML (str);   fs.writeFile ("b.html" , html, function  (err ) {     if  (err) {       return  console .log (err);     }     console .log ("write.success" );   }); }); 
 
ES6 语法简化处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 let  fs = require ("fs" );let  markdown = require ("markdown" ).markdown ;fs.readFile ("a.md" , "UTF-8" , (err, str ) =>  {   if  (err) {     return  console .log (err);   }   let  html = markdown.toHTML (str);   fs.writeFile ("b.html" , html, (err ) =>  {     if  (err) {       return  console .log (err);     }     console .log ("write.success" );   }); }); 
 
Promise 处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 const  fs = require ("fs" );const  markdown = require ("markdown" ).markdown ;readFile ("a.md" )  .then ((mdStr ) =>  {     return  markdown.toHTML (mdStr);    })   .then ((html ) =>  {     writeFile ("b.html" , html);   })   .catch ((e ) =>  {     console .log (e);   }); function  readFile (url ) {  return  new  Promise ((resolve, reject ) =>  {     fs.readFlie (url, "UTF-8" , (err, str ) =>  {       if  (err) {         reject (new  Error ("readFlie error" ));       } else  {         resolve (str);       }     });   }); } function  writeFile (url, data ) {  return  new  Promise ((resolve, reject ) =>  {     fs.writeFile (url, data, (err, str ) =>  {       if  (err) {         reject (new  Error ("write error" ));       } else  {         resolve ();       }     });   }); } 
 
使用模块改装上面代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const  markdown = require ("markdown" ).markdown ;const  fsp = require ("fs-promise" ); let  onerror  = (err ) => {  console .error ("something wrong" ); }; fsp   .readFile ("a.md" , "UTF-8" )   .then ((mdStr ) =>  {     return  markdown.toHTML (mdStr);   })   .then ((html ) =>  {     fsp.writeFile ("b.html" , html);   })   .catch (onerror); 
 
async/await 处理 1 2 3 4 5 6 7 8 9 10 11 12 13 const  markdown = require ("markdown" ).markdown ;const  fsp = require ("fs-promise" );let  onerror  = (err ) => {  console .error ("sonething wrong" ); }; async  function  start ( ) {  let  mdStr = await  fsp.readFile ("a.md" , "UTF-8" );   let  html = markdown.toHTML (mdStr);   await  fsp.writeFile ("b.html" , html); } start ().catch (onerror);
 
方法 Promise.prototype.then(onFulfilled, onRejected) 接收成功或失败的结果回调到当前 promise, 返回一个新的 promise, 将以回调的返回值来 resolve.
Promise.prototype.catch(onRejected) 接受一个失败的结果回调到当前 promise, 返回一个新的 promise。当这个回调函数被调用,新 promise 将以它的返回值来 resolve,否则如果当前 promise 进入 fulfilled 状态,则以当前 promise 的完成结果作为新 promise 的完成结果.
Promise.prototype.finally(onFinally) 添加一个事件处理回调于当前 promise 对象,并且在原 promise 对象解析完毕后,返回一个新的 promise 对象。回调会在当前 promise 运行完毕后被调用,无论当前 promise 的状态是完成(fulfilled)还是失败(rejected)
async/await async 函数的创建时通过在函数声明语句之前加上 async 关键字,
这是异步函数的特征之一 ––它将任何函数转换为 promise。
示例如下:
1 2 3 4 5 6 7 const  asyncFunction  = async  ( ) => {   }; async  function  asyncFunction ( ) {   } 
 
await async 异步函数可通过await来暂停,该关键字只能用在 async 函数内部。
每当函数执行完毕,await返回的是任何 async 函数会返回的东西。
阮一峰老师的解释: async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句
 
只能放在 async 函数内部使用,不能放在普通函数里面,否则会报错。
 
后面放 Promise 对象,在Pending状态时,相应的协程会交出控制权,进入等待状态。等等党永不为奴!这个是本质。
await是 async wait 的意思,wait 的是resolve(data)消息,并把数据data返回。
await后面也可以跟同步代码,不过系统会自动转化成一个 Promise 对象。
await只关心异步过程成功的消息resolve(data),拿到相应的数据data。至于失败消息reject(error),不关心,不处理。
await 的特点 1.建立在 promise 之上。所以,不能把它和回调函数搭配使用。但它会声明一个异步函数,并隐式地返回一个 Promise。因此可以直接 return 变量,无需使用 Promise.resolve 进行转换。
2.和 promise 一样,是非阻塞的。但不用写 then 及其回调函数,这减少代码行数,也避免了代码嵌套。而且,所有异步调用,可以写在同一个代码块中,无需定义多余的中间变量。
3.它的最大价值在于,可以使异步代码,在形式上,更接近于同步代码。
4.它总是与 await 一起使用的。并且,await 只能在 async 函数体内。
5.await 是个运算符,用于组成表达式,它会阻塞后面的代码。如果等到的是 Promise 对象,则得到其 resolve 值。否则,会得到一个表达式的运算结果。
MDN 例子:
1 2 3 4 5 6 async  function  hello ( ) {  return  (greeting = await  Promise .resolve ("Hello" )); } hello ().then (alert);
 
简单案例:
1 2 3 4 5 6 7 8 9 10 11 12 function  timeout (ms ) {  return  new  Promise ((resolve ) =>  {     setTimeout (resolve, ms);   }); } async  function  asyncPrint (value, ms ) {  await  timeout (ms);   console .log (value); } asyncPrint ("hello world" , 50 );